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:
- 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
- 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
- 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
- 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
- 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 it
noImplicitThis
This 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 one
moduleResolution
Classic (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.
- 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:
declare var
Declare global variables- 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
- Typescript tutorial
- Use class types in generics
- You’ve used several of these handy TypeScript built-in generic helper types
- Typescript handbook
- Typescript Handbook Chinese Edition