Declarations are typescript’s type language that describes the types of variables, functions, and modules provided by the library. Javascript has no type, and many existing libraries are developed in javascript. How to know the corresponding type when using typescript to reference such libraries is through declaration files. There are three key points:
-
Declaration file format: [filename].d.ts.
-
It essentially acts as a middleman, providing type specifications for javascript
-
Some projects are developed directly in typescript, while others use declaration files to plan type design, separating type declaration from business logic
Focusing only on type design when designing a type is purely declarative, giving typescript absolute abstraction and no attention to implementation details is a great aid to team collaboration.
Whether you’re providing a type specification for javascript or designing it as a type, it’s essentially the same, just the purpose of use.
The statement reference
Some commonly used data formats (e.g. object methods/properties, functions), let’s look at their declaration file writing.
Object property method
// Provide methods/attributes on an object
export const obj = {
getGreeting(str) {
return `hello ${str}`;
},
count: 10};/** * Declare namespace to describe the type or value accessed by the dot. * /
declare namespace myLib {
function getGreeting(srt: string) :string;
let count: number;
}
Copy the code
Function overloading
function getTypeList(input) {
if (typeof input === "string") {
return `hello world of ${input}`;
} else if (typeof input === "number") {
return[input]; }}Function overloading: the function name is the same, the argument is different, the return type is different */
declare function getTypeList(str: string) :string;
declare function getTypeList(num: number) :number[];
Copy the code
Tissue types
When you have a large library, you want to think about splitting it up into different namespaces, different interfaces based on responsibilities; This reduces complexity and facilitates maintenance.
// Use namespaces to organize types into different interfaces
/ statement written in the * * * * commodity classification, we list the two classes, in the same namespace, with no interface to declare * /
declare namespace Production {
/ / computer
interface Computer {
/ / kernel number
core: number;
/ / size
size: string;
}
/ / fruit
interface Apple {
/ / unit price
price: number; }}// Also supports nested namespaces, accessed via dots
declare namespace Production.Fruit {
interface Banana {}
interface Orange {}
}
Copy the code
Declaration of a class
// Class on business logic
class Greeting {
word = "";
constructor(str: string) {
this.word = str;
}
echo(str: string) {
console.log(str || this.word); }}Constructor, method, attribute declaration */
declare class Greeting {
constructor(str: string);
word: string;
echo(str: string) :void;
}
Copy the code
The global variable
var globalVar = 'hello world'
/** ** ** /
declare var globalVar: string;
Copy the code
Global function
// in the global scope
var greet = (str) = > console.log(str); =/** ** ** /
declare function greet(greeting: string) :void;
Copy the code
Declare is used globally, and namespace is used locally to prevent conflicts. It can be used as required.
Library structure analysis
When writing a declaration file for a JavaScript library, we should first determine the type of the library, whether it is commonJS, ES module, or global class library, UMD Y library. They all have some main characteristics, which we can analyze at the following code level:
- Without an explicit reference, use require or define directly anywhere
- Use import * as a from ‘b’; Or export c.
- Exports or module.exports assigns a value
- Assign directly to the window or global object,
Identify modular libraries
It is mainly a function, a class class, commonJS, ES module, files are given the corresponding template. The declaration reference in front of the parameter, written on the basis of understanding, is relatively simple.
- Var fs = require(“fs”);
- Documentation describing how to require or import a library
- Exports or module.exports assigns a value
- Use import * as a from ‘b’; Or export c.
See the global library
The global library can take some clues from the keywords declared on the call:
- Top-level VAR statement or function declaration
- Call window.someName, directly from anywhere
- DOM primitives like the assumption that document or Window exist
Identify the global library UMD
In the code, there is an immediate execute function:
(function (root, factory) {
if (typeof define === "function" && define.amd) {
define(["libName"], factory);
} else if (typeof module= = ="object" && module.exports) {
module.exports = factory(require("libName"));
} else {
root.returnExports = factory(root.libName);
}
}(this.function (b) {
Copy the code
Declare file dependencies
A declaration file depends on other declaration files, which can be imported differently depending on the class library
Dependencies on global libraries
If your library depends on the global library, use the /// directive
/// <reference types="someLib" />
function getThing() :someLib.thing;
Copy the code
Dependency on modules
If your library depends on a standard ES module, use the following import statement:
import * as moment from "moment";
function getThing() :moment;
Copy the code
Dependencies on UMD libraries
/// <reference types="moment" />
function getThing() :moment;
Copy the code
ES6 impact on module call signatures
// Namespace import is like import * as moment from "moment" const moment = require("moment")
The ES6 module specification states that the namespace import (import * as x) can only be an object and cannot be executed
import exp = require("express");
var app = exp();
Copy the code
In es6-compliant module loaders, top-level objects (imported here as exp) can only have properties; Top-level module objects can never be called.
Opening esModuleInterop solves both of these problems in TypeScript translated code.
- The first one changed the behavior of the compiler,
- The second is fixed with two new helper functions that provide a shim to ensure compatibility of emitted JavaScript:
Links to templates
- The module
- Modules: plug-ins
- Class module:
- Modules: functions
- Global class library
Abstract ability
Typescipte’s abstraction capability comes in handy when a new requirement is clarified and the business is usually abstracted. Some of the requirements are not specified yet, and some of the interface fields that depend on the background are still being discussed, but development is already underway. Design business modules, abstract classes, abstract parameters
interface LoginResponse {
userId: number;
name: string;
}
interface LoginParams {
name: string;
pwd: string;
}
abstract class AbstractLogin {
/ / login
abstract login(params: LoginParams): Promise<LoginResponse>;
/ / logout
abstract logout(): Promise<boolean>;
}
* this is an important ability to focus on a point first, and the rest can be dealt with later
export class LoginUser implements AbstractLogin {
login(params: LoginParams) {
// do something, without going into details, actually call the background login interface
console.log(params);
return Promise.resolve({ userId: 1.name: "hello world" });
}
logout() {
// Implementation details: such as empty token, call to background interface logout, permission logout
// We can shelve it, we can provide it later, or someone else can implement it
// Reduce maintenance costs
return Promise.resolve(true); }}Copy the code
conclusion
When deciding to write a declaration file for a library, the first step is to identify the library based on its characteristics, and then find the corresponding module to refer to. Of course, if you know how to make a statement, it’s better to just organize it. In fact, many third libraries provide declarative files. Nowadays, writing declarative files is more about type design, providing business abstraction for developers.
If there are any mistakes or omissions in this article, please correct them. Thank you.