This article summarizes TypeScript type declaration writing. In many cases, TypeScript writing is not a problem, but TypeScript writing is particularly difficult. Let me summarize some of the problems I have with TypeScript. If you encounter type declarations that don’t work, take a look at loDash’s declarations, because LoDash does all sorts of manipulation on the data, so you’ll find examples.

Basic types of

 / / variable
 const num: number = 1;
 const str: string = 'str';
 const bool: boolean = true;

 const nulls: null = null;
 const undefine: undefined = undefined;
 const symbols: symbol = Symbol('symbal');

 const any: any = 'any types'; The typescript any type has no type constraints
Copy the code

An array of

 // Array: T[] is recommended
 const nums: number[] = [1.2.3.4];

 // Not recommended: Array
      
        is generic. It is incompatible with JSX, so T[] is used for consistency
      
 const strs: Array<string> = ['s'.'t'.'r'];

 const dates: Date[] = [new Date(), new Date()];
Copy the code

Array concat method, return type never[] problem

 // Array concat method never problem
 // prompt: Type 'string' is not assignable to Type 'never'.
 const arrNever: string[] = [].concat(['s']);

 // The main problem is: [] array, ts cannot determine the type of elements in the array according to the context
 // @see https://github.com/Microsoft/TypeScript/issues/10479
 const fixArrNever: string[] [] = (as string[]).concat(['s']);
Copy the code

interface

Interfaces are a core component of TypeScript, which can merge multiple type declarations into one:

And interfaces can be used to declare data types such as functions, classes, and objects

interface Name {
  first: string;
  second: string;
}

let username: Name = {
  first: 'John',
  second: 'Doe'
};


Copy the code

Any, NULL, undefined, void

// Special type
const any: any = 'any types'; // Typescript any type, equivalent to writing nothing at all
let nobody: any = 'nobody, but you';
nobody = 123;

let nulls: number = null;
let bool: boolean = undefined;

// void
function printUsername (name: string) :void {
    console.log(name);
}
Copy the code

The joint type

Joint type in option bags very practical model scene, marked by using | * * * *

function options(opts: { types? :string;
    tag: string | number;
}) :void {}Copy the code

Cross type

The most typical usage scenarios are inheritance and mixins, or operations such as copy

// Assign: If you can't write this type in the future, just look at object. assign
function $extend<T.U> (first: T, second: U) :T & U {
  return Object.assign(first, second); // Just a hint
}
Copy the code

Tuples tuple

Tuples are rarely used

let nameNumber: [string.number];

// Ok
nameNumber = ['Jenny'.221345];

// Error
// nameNumber = ['Jenny', '221345'];

let tuple: [string.number];
nameNumber = ['Jenny'.322134];

const [usernameStr, uselessNum] = nameNumber;
Copy the code

The type of role

You can also rename (alias) existing types. It is recommended to use type to create simple types, unnested types, or a layer of nested types. Other complex types should be implemented using interface, combined with implements and extends.

type StrOrNum = string | number;

/ / use
let sample: StrOrNum;
sample = 123;
sample = '123';

// Check the type
sample = true; // Error
Copy the code

Problems encountered in practice

The third party library does not provide the declaration D. ts file

If a third-party library does not provide declaration file, the first time to https://github.com/borisyankov/DefinitelyTyped for the Microsoft’s official warehouse, or on npmjs.com search @ types/dependent module name most cases can be found.

  • Add the declaration file manually

    Declaration files are generally placed in the types folder

    // Example: types/axios.d.ts
    declare module'axios'; // Axios is declared to be of type anyCopy the code

The global variable

For example, some libraries simply add global variables to a window

// globals.d.ts
// Example: jQuery. In real life, jQuery has.d.ts
declare const jQuery: any;
declare const $: typeof jQuery;
Copy the code

The JavaScript resources

In front-end engineering, when importing many non-JS resources, such as CSS, HTML, images and VUE, which cannot be recognized by TS, it is necessary to tell TS how to identify the types of imported resources.

// See how vue handles this: shims-vue.d.ts
declare module '*.vue' {
  import Vue from 'vue';
  export default Vue;
}

// html
declare module '*.html';
// css
declare module '*.css';
Copy the code

Cast casting

Sometimes casts are needed, especially for associated types or optional attributes.

// First: use <> parentheses
const convertArrType: string[] = <Array<string>>[].concat(['s']);

// Use the as keyword
const fixArrNever: string[] [] = (as string[]).concat(['s']);
Copy the code

I recommend using the second one because it is compatible with JSX, and the first one is officially not recommended even though it is legal.

Optional and default properties

Many of the parameters provided in the API have default values or optional attributes.

class Socket {}
// Union type
export type SocketType = 'WebSocket' | 'SockJs';

export interface SocketOptions {
  type: SocketType; protocols? :string | string[]; / / is optional
  pingMessage: string | (() = >string); // Union type, which can bestringOr functionpongMessage: string | (() = >string); } // Default valueexport function eventHandler = (
  evt: CloseEvent | MessageEvent | Event,
  socket: Socket,
  type = 'WebSocket' / / the default value
) = > any;
Copy the code

How do independent functions declare their types

I struggled with this at first, because I’m just a separate function, so how do I declare a type? Especially when writing apis for event handlers.

class Socket {}
// How a function is declared
export type SocketEventHandler = (
  evt: CloseEvent | MessageEvent | Event,
  socket: Socket
) => any;

const eventHandler: SocketEventHandler = (evt, socket) = >{}// Optional arguments and REST arguments
let baz = (x = 1) = > {};
let foo = (x: number, y: number) = > {};
let bar = (x? :number, y? :number) = > {};
let bas = (. args:number[]) = > {};
Copy the code

Index attribute type declaration

Objects in JavaScript can use string indexes to fetch properties or call methods directly, and TypeScript has type-declaration methods.

type Hello = {
    hello: 'world';
    // Key is just a formal attribute name (like a parameter)
    [key: string] :string;
};

const greeting: Hello = {
    hi: 'morning'
}

console.log(greeting['hi'])
Copy the code

Dynamically added property declarations

Sometimes we just declare a basic type structure, and then we extend it, especially options in secondary encapsulation.

interface AxiosOptions {}

type AjaxOptions = {
   axiosOptions: AxiosOptions;
   // Add additional extensions to separate attribute nodes
   extraOptions: {
       [prop: string] :any
   }; 
};

typeAjaxOptions1 = { axiosOptions? : AxiosOptions;// Don't do this, because TS will not tell you when axiosOptions is misspelled
  // Try to move subsequent extended attributes under separate attribute nodes
  [prop: string] :any
};

const ajaxOptions: AjaxOptions1 = {
  axiosOptions1: {}; // It is meant to be axiosOptions, but ts does not prompt
}
Copy the code

! The use of

! The identifier tells the TS compiler that there is no problem with the declared variable and that no errors will be reported during re-run.

class BaseSelect extends Vue {
    options: string[]; // No initialization was done in constructor
    
    created() {
        this.options = ['inited']}}class BaseSelect extendsVue { options! :string[]; / / use! Tell the compiler I know what I'm doing, okay
    
    created() {
        this.options = ['inited']}}Copy the code

The use of this

For functions that are used independently, you can declare a specified calling context

class Handler {
    info: string;
    // Declare the specified this context
    onClickBad(this: Handler, e: Event) {
        // oops, used this here. using this callback would crash at runtime
        this.info = e.message; }}let h = new Handler();
uiElement.addClickListener(h.onClickBad); // error!
Copy the code

Declaration merge (extending Vue declaration)

Take a look at the usage scenarios, extending the VUE, adding global attributes to the VUE.

// Vue is declared in vue/types/vue.d.ts

declare module 'vue/types/vue' {
  $eventBus = Vue.$eventBus
  interface Vue {  
    $eventBus: Vue;
  }
    
  // equivalent to Vue. Prototype.$eventBus
  interfaceVueConstructor { $eventBus: Vue; }}Copy the code

Ignore TypeScript validation

Use // @ts-check to opt in to type checking for a single file.
Use // @ts-nocheck to opt out of type checking for a single file.
Use // @ts-ignore to opt out of type checking for a single line.
Copy the code

conclusion

There are many more advanced uses of TypeScript declarations that I don’t currently use that much. When I’m struggling with how to write a declaration, I look at how other people’s declaration files are written.

Note: try not todeconstructionandThe statementWritten together, it is very unreadable.

class Node {
  onNodeCheck(checkedKeys: any, { / / deconstruction
      checked, checkedNodes, node, event,
    } : { / / declare
      node: any;
      [key: string] :any; }) {}}Copy the code

Pay attention to the public number, find more wonderful content.