How do I write a declaration file

For third-party modules, there are two types of declaration files, one is written under @types/, and the other is written in the module’s own project. When we reference a third-party package, we check to see if the package has a declaration file. If not, we have to write our own. There are several cases as follows:

  1. Global class library writing
// globalLib.js
function globalLib(options) {
    console.log(options)
}
globalLib.version = '1.2.1'
globalLib.doSomthing = function() {}
Copy the code

Add a new globallib.d. ts declaration file in the same directory as globallib.js

// globalLib.d.ts
// Global declaration
declare function globalLib(options: globalLib.Options) :vold;
// namespace (function and namespace declaration merge)
declare namespace globalLib {
    const version: string;
    function doSomthing() :void;
    // Indexable interface that can accept any string attributes and return any
    interface Options {
        [key: string] :any}}Copy the code
  1. Module class library
// moduleLib.js
function moduleLib(options) {
    console.log(options)
}
moduleLib.version = '1.2.1'
moduleLib.doSomthing = function() {}
module.exports = moduleLib
Copy the code
// moduleLib.d.ts
// Module declaration
declare function moduleLib(options: moduleLib.Options) :vold;
// Declare that the file itself is a module, so the interface is not exposed
interface Options {
    [key: string] :any
}
// namespace (function and namespace declaration merge)
declare namespace moduleLib {
    const version: string;
    function doSomthing() :void;
}

export = moduleLib
Copy the code
  1. Umd library to write
// umdLib.js
(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(factory)
    } else if (typeof module= = ='object' && module.exports) {
        module.exports = factory()
    } else {
        root.umdLib = factory()
    }
}(this.function() {
    return {
        version: '1.2.1'.doSomthing: function() {}}}))Copy the code
// umdLib.d.ts

declare namespace umdLib {
    const version: string;
    function doSomthing() :void;
}
// This statement is written for the umD library.
export as namespace umdLib
export = umdLib
Copy the code

When referring to the UMD library as a global variable, turn on the allowUmdGlobalAccess configuration in tsconfig.json, otherwise an error will be reported

  1. Add custom methods to the module library
import moment from 'moment'
declare module 'moment' {
    export function myFunction(name: string) :string
} 
moment.myFunction = (name: string) = >name + 's'
Copy the code
  1. Add methods to global variables
declare global {
    namespace globalLib {
        function doAnything: void}}globalLib.doAnything = () = >{}
Copy the code

Detailed explanation of compilation items

options type The default value describe
files array A list of individual files that the compiler needs to compile
include array The file or directory that the compiler needs to compile. Wildcard characters such as SRC/are supported, indicates that only the level-1 directories under the SRC directory are compiled. src//* Indicates that only ts files in the secondary directory are compiled. SRC compiles all ts files in the SRC directory
exclude array Node_modules, and all declaration files A list of individual files that the compiler needs to compile
extends string Configuration inheritable, such as ‘./tsconfig.base.json’
incremental boolean Incremental compilation, which can optimize the compilation speed, the second compilation speed will be greatly improved
diagnostics boolean Prints compiled diagnostic information
tsBuildInfoFile string The name location of the incremental compilation file
traget string ES3 Specify the target version of ECMAScirpt: ES3, ES5, ES6 (ES2015), ES2016, ES2017 orESNext
module string traget === ‘ES6’ ? ‘ES6’ : ‘commonjs’ Specify the generated code module standard: None, CommonJS, AMD, System, UMD, ES6, ESNext
lib string[] The default injected libraries are: (1) target ES5: DOM, ES5, ScriptHost (2) Target ES6: DOM, ES6, DOM.Iterable, ScriptHost List of library files that need to be imported during compilation: ES5, ES6 (ES2015), ES7 (ES2106), ES2017, ES2018, ESNext, DOM, DOM.Iterable, WebWorker, ScriptHost, ES2015.Core, ES2015. See the typescript source code.
allowJs boolean false Allows compiling js files
jsx string preserve JSX is supported in.tsx files: ‘preserve’, ‘react-native’, ‘react’, ‘react- JSX ‘or ‘react-jsxdev’.
outDir string Redirect the output directory
rootDir string The directory structure –outDir — is used only to control the output
moduleResolution string module === “AMD” or “System” or “ES6” ? “Classic” : “Node” Decide what to doModule parse
baseUrl string The base directory for resolving non-relative module names
paths Object List of module names to baseUrl based path maps
types string[] List of type declaration filenames to include. If types is specified, only the specified content will be looked up under @types/, if no types will be looked up under @types
composite boolean Support engineering Citation
  • So let’s parse itnoImplicitThisThis configuration does not allow this to have an implicit any type.
class A {
    constructor() {
        this.a = 'Lizzy'
    }
    getA() {
        return function() {
            console.log(this.a)
        }
    }
}
const foo = new A().getA()

foo()
Copy the code

At this point, executing this code on the console will return an error because the reference to this has changed. You need to change it to an arrow function, and you won’t have a problem. NoImplicitThis is set to true, and the compiler will report errors during code writing to prevent such errors.

class A {
    constructor() {
        this.a = 'Lizzy'
    }
    getA() {
        return () = > {
            console.log(this.a)
        }
    }
}
const foo = new A().getA()

foo()
Copy the code
  • Let’s do another onemoduleResolutionClassic (AMD, SystemJs, ES6) and Node resolve files differently, as shown below:

The TS configuration in VUe3 is described as an example:

{
  "compilerOptions": {
    "baseUrl": ".".// Resolve the base address of non-relative modules, default current directory
    "outDir": "dist".// Specify a directory for the output file
    "sourceMap": false.// Generate the sourceMap of the target file
    "target": "esnext"."module": "esnext"."moduleResolution": "node".// The parsing strategy of the module
    "allowJs": false."strict": true.// Enable all strict type checking -> inject strict use strict into code, do not allow implicit any, do not allow null, undefined to other types of variables, do not allow this to have implicit any type
    "noUnusedLocals": true."experimentalDecorators": true."resolveJsonModule": true."esModuleInterop": true."removeComments": false.// Delete comments
    "jsx": "preserve"."lib": ["esnext"."dom"]."types": ["jest"."puppeteer"."node"].// Controls the directory of declaration files. If specified, only the specified declaration file is found
    "rootDir": ".".// Specify the input file directory
    "paths": { // Path mapping, relative to baseUrl
      "@vue/*": ["packages/*/src"]."vue": ["packages/vue/src"]}},"include": [
    "packages/global.d.ts"."packages/*/src"."packages/runtime-dom/types/jsx.d.ts"."packages/*/__tests__"."test-dts"]}Copy the code

Typescript core library (Lib) types in detail

Typescript defines types for DOM, JavaScript built-in objects, and so on. This is the –lib option of the configuration item above, which configures the library files that need to be imported during compilation.

  1. DOM

For example, when we get the DOM element app using document.getelementByid (‘app’), its type in Typescript is the HTMLElement interface

As follows, HTMLElement interface Element inherited, DocumentAndElementEventHandlers ElementCSSInlineStyle, ElementCSSInlineStyle, ElementContentEditable, GlobalEventHandlers, HTMLOrSVGElement, and declare var HTMLElement: {} declare global variables. It’s an interesting way to quickly learn about the structure of DOM objects by looking at these interfaces in Typescript.

The source code is as follows:

/** Any HTML element. Some elements directly implement this interface, while others implement it via an interface that inherits it. */

interface HTMLElement extends Element, DocumentAndElementEventHandlers, ElementCSSInlineStyle, ElementCSSInlineStyle, ElementContentEditable, GlobalEventHandlers, HTMLOrSVGElement {
    accessKey: string;
    readonly accessKeyLabel: string;
    autocapitalize: string;
    dir: string;
    draggable: boolean;
    hidden: boolean;
    innerText: string;
    lang: string;
    readonly offsetHeight: number;
    readonly offsetLeft: number;
    readonly offsetParent: Element | null;
    readonly offsetTop: number;
    readonly offsetWidth: number;
    spellcheck: boolean;
    title: string;
    translate: boolean;
    click(): void;
    addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) = > any, options? :boolean | AddEventListenerOptions): void;
    addEventListener(type: string.listener: EventListenerOrEventListenerObject, options? :boolean | AddEventListenerOptions): void;
    removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) = > any, options? :boolean | EventListenerOptions): void;
    removeEventListener(type: string.listener: EventListenerOrEventListenerObject, options? :boolean | EventListenerOptions): void;
}

declare var HTMLElement: {
    prototype: HTMLElement;
    new(): HTMLElement;
};

Copy the code

For the above source code, there are a few knowledge points, I list here:

  1. declare varDeclare global variables
  2. Some elements implement the HTMLElement interface directly, while others implement interfaces that inherit from HTMLElement:

For example, the div we generate with document.createElement(‘div’) is an HTMLDivElement interface. Its inheritance relationship is shown as follows:

The lib.dom.d.ts declaration file in Typescript already defines declarations for these relationships.

interface HTMLDivElement extends HTMLElement {
    /** * Sets or retrieves how the object is aligned with adjacent text. */
    / * *@deprecated * /
    align: string;
    addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLDivElement, ev: HTMLElementEventMap[K]) = >any, options? : boolean | AddEventListenerOptions):void;
    addEventListener(type: string, listener: EventListenerOrEventListenerObject, options? : boolean | AddEventListenerOptions):void;
    removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLDivElement, ev: HTMLElementEventMap[K]) = >any, options? : boolean | EventListenerOptions):void;
    removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options? : boolean | EventListenerOptions):void;
}

declare var HTMLDivElement: {
    prototype: HTMLDivElement;
    new(): HTMLDivElement;
};
Copy the code

ESNext

ESNext in Typescript includes es2020, esNext-intl, and so on, as shown by the triple slash directive:

///
, which allows files to explicitly include existing built-in lib files.
This directive marks the file as the default library. You’ll see this comment at the top of lib.d.ts and its various variants.

  • Module dependencies:/// <reference types="sizzle" />, the search method is innode_modules/@types/Find the corresponding declaration file in the Sizzle directory
  • Path dependence:/// <reference path="legacy.d.ts" />The legacy. D. ts declaration file will be found in the same directory as this file
/// <reference no-default-lib="true"/>

/// <reference lib="es2020" />
/// <reference lib="esnext.intl" />
/// <reference lib="esnext.string" />
/// <reference lib="esnext.promise" />
/// <reference lib="esnext.weakref" />
Copy the code

Typescript has built-in generics

Here’s a look at some tool generics and their implementation. Most of these generic interface definitions are syntax-sugar (shorthand), which you can find in the typescript package lib.es5.d.ts

Partial

/** * Make all properties in T optional */
type Partial<T> = {
    [P inkeyof T]? : T[P]; };Copy the code

Required

/** * Make all properties in T required */
type Required<T> = {
    [P inkeyof T]-? : T[P]; };Copy the code

Readonly

/** * Make all properties in T readonly */
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};
Copy the code

Pick

/** * From T, pick a set of properties whose keys are in the union K */
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};
Copy the code

Record

/** * Construct a type with a set of properties K of type T */
type Record<K extends keyof any, T> = {
    [P in K]: T;
};
Copy the code

Exclude

/** * Exclude from T those types that are assignable to U */
type Exclude<T, U> = T extends U ? never : T;
Copy the code

Extract

/** * Extract from T those types that are assignable to U */
type Extract<T, U> = T extends U ? T : never;
Copy the code

Omit

/** * Construct a type with the properties of T except for those in type K. */
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
Copy the code

NonNullable

/** * Exclude null and undefined from T */
type NonNullable<T> = T extends null | undefined ? never : T;
Copy the code

Parameters

/** * Obtain the parameters of a function type in a tuple */
type Parameters<T extends(... args: any) => any> = Textends(... args: infer P) => any ? P : never;Copy the code

ConstructorParameters

/** * Obtain the parameters of a constructor function type in a tuple */
type ConstructorParameters<T extends new(... args: any) => any> = Textends new(... args: infer P) => any ? P : never;Copy the code

ReturnType

/** * Obtain the return type of a function type */
type ReturnType<T extends(... args: any) => any> = Textends(... args: any) => infer R ? R : any;Copy the code

InstanceType

/** * Obtain the return type of a constructor function type */
type InstanceType<T extends new(... args: any) => any> = Textends new(... args: any) => infer R ? R : any;Copy the code

Further reading

  1. Typescript tutorial
  2. Use class types in generics
  3. You’ve used several of these handy TypeScript built-in generic helper types
  4. Typescript handbook
  5. Typescript Handbook Chinese Edition