How to write a Typescript declaration file

Source: Internet
Author: User

Use TypeScript has been a while, this is really a good thing, although the use of the process also found some bug , but are small problems, so the overall experience is very good.

TypeScriptIt Type is called, and its strong type is inseparable, this is also different from the JavaScript most critical point, the type of declaration can be directly written in the code, you can also write a separate description of the type of the file *.d.ts .

Common ways

In the first place there d.ts are no simple basic type definitions (since these are written in expressions, variables, and there is no meaning in the definition), and the declarations are often defined in the types of complex structures.

Most of the syntax is consistent with the syntax written in ts the normal file and is export followed by the member to be exported.

The simplest is to use type keywords to define:

type A = {                 // 定义复杂结构  b: number  c: string}type Func = () => number   // 定义函数type Key = number | string // 多个类型
Combination type

And there's a TypeScript very easy way to reuse it in type , for example, we have a Animal type, and a Dog type that you can use & to reuse.

P.s> & symbols can be stitched together with multiple

type Animal = {  weight: number  height: number}type Dog = Animal & {  leg: number}
Dynamic JSON type designation

If we have a JSON structure, and it's key dynamic, then we certainly can't key write everything in the code, we just need to simply specify a wildcard character:

type info = {  [k: string]: string | number // 可以指定多个类型}const infos: info = {  a: 1,  b: '2',  c: true, // error 类型不匹配}

It is also recommended to use built-in functions for the new version Record :

const infos: Record<string, string | number> = {  a: 1,  b: '2',  c: true, // error}
Gets the type of the variable

If we have a JSON object that contains name age two properties, we can TypeScript implement some interesting things through some built-in tool functions.

keyofwith the typeof combination we can get the results we want:

const obj = {  name: 'Niko',  age: 18}// 如果是这样的取值,只能写在代码中,不能写在 d.ts 文件中,因为声明文件里边不能存在实际有效的代码type keys = keyof typeof objlet a: keys = 'name' // passlet b: keys = 'age'  // passlet c: keys = 'test' // error

JSON JSON This can also be used if we want to modify a type that is not uniform to a uniform type:

const obj = {  name: 'Niko',  age: 18,  birthday: new Date()}const infos: Record<keyof typeof obj, string> = {  name: '',  age: '',  birthday: 123, // 出错,提示类型不匹配  test: '', // 提示不是`info`的已知类型}
Gets the return value type of the function

For example, if we have a function, the function returns one JSON , and we need this to be the JSON type.

Then it can be ReturnType<> achieved by:

function func () {  return {    name: 'Niko',    age: 18  }}type results = ReturnType<typeof func>// 或者也可以拼接 keyof 获取所有的 keytype resultKeys = keyof ReturnType<typeof func>// 亦或者可以放在`Object`中作为动态的`key`存在type infoJson = Record<keyof ReturnType<typeof func>, string>
Declaring a function in code and classType

Because we know that functions and class when they are created have actual code (function body, constructor).
But we are writing in the d.ts declaration file, this is only a constraint on the type, so there must be no real code, but if it is written in the ordinary ts file will be wrong, so in order to this kind of situation, we need to use the declare keyword, It means that we are here to define a type, not an object, a function:

class Personal {  name: string  // ^ 出错了,提示`name`必须显式的进行初始化}function getName (personal: Personal): name// ^ 出错了,提示函数缺失实现

The following are the correct ways to use:

-declare class Personal {+declare class Personal {  name: string}-function getName (personal: Personal): name+declare function getName (personal: Personal): name

Of course, in general, it is not recommended that class the definition, should be used interface instead of it, this class should only exist in the description of non- TS modules, if it is a module developed by itself, then the structure of itself has the characteristics of the declaration type.

function overloading

This concept is in some strongly-typed languages, depending on TypeScript , this is also a strong type of language, so there will be a need to use this kind of declaration place.

For example, we have a add function that can be used to string splice the parameters of the type, or to number add the parameters of the receiving type.

It is important to note that the definition of functions can be placed in a file only when the function overload definition of a third-party plug-in is made, and the implementation of the d.ts function is recommended in other contexts (although the configuration paths can be handled separately, it loses the constraint on the creation of the function)

// index.ts// 上边是声明function add (arg1: string, arg2: string): stringfunction add (arg1: number, arg2: number): number// 因为我们在下边有具体函数的实现,所以这里并不需要添加 declare 关键字// 下边是实现function add (arg1: string | number, arg2: string | number) {  // 在实现上我们要注意严格判断两个参数的类型是否相等,而不能简单的写一个 arg1 + arg2  if (typeof arg1 === 'string' && typeof arg2 === 'string') {    return arg1 + arg2  } else if (typeof arg1 === 'number' && typeof arg2 === 'number') {    return arg1 + arg2  }}

TypeScriptFunction overloading is also just the declaration of multiple functions, the specific logic also need to write their own, he will not really have your multiple functions of the same function of the merging of the body

Order problems for multiple functions

Imagine if we had a function that passed the parameter of the Date type, returned its timestamp unix , if passed in Object , then output the object's specific type, and the toString rest would return directly, what should a function like this be written?

Just a sample demonstration, normal people will not write such a function ...

function build (arg: any) {  if (arg instanceof Date) {    return arg.valueOf()  } else if (typeof arg === 'object') {    return Object.prototype.toString.call(arg)  } else {    return arg  }}

But such a function overload in the order of the Declaration is very fastidious, it is important to put the accuracy of high in front:

// 这样是一个错误的示例,因为无论怎样调用,返回值都会是`any`类型function build(arg: any): anyfunction build(arg: Object): stringfunction build(arg: Date): number

Because TypeScript after finding a declaration of a function overload, it stops and does not continue to find, any is a most ambiguous range, and is Object contained Date , so we should arrange from small to large in order:

function build(arg: Date): numberfunction build(arg: Object): stringfunction build(arg: any): any// 这样在使用的时候才能得到正确的类型提示const res1 = build(new Date()) // numberconst res2 = build(() => { })  // stringconst res3 = build(true)       // any
Some scenarios that do not require function overloading

The meaning of function overloading is to let you know that passing in different parameters results in a different result, but if the arguments passed in are different, but the resulting ( type ) is the same, then you should not use function overloading (meaningless).

If the function has the same return value type, then you do not need to use function overloading

function func (a: number): numberfunction func (a: number, b: number): number// 像这样的是参数个数的区别,我们可以使用可选参数来代替函数重载的定义function func (a: number, b?: number): number// 注意第二个参数在类型前边多了一个`?`// 亦或是一些参数类型的区别导致的function func (a: number): numberfunction func (a: string): number// 这时我们应该使用联合类型来代替函数重载function func (a: number | string): number
Interface

interfaceIs in the TypeScript middle of the exclusive, in JavaScript and not interface a say.
Because it interface is only used to specify the corresponding behavior of implementing it class , there is no real code, which is an invalid operation for the scripting language.

There is not much difference in syntax, class but only in the declaration of interface member properties, such as the function ability to write specific received parameters and the type of return value, can not be written in the interface specific function body, the same, for member properties can not directly in the interfaceto assign values in:

// 这是一个错误的示例interface PersonalIntl {  name: string = 'Niko'  sayHi (): string {    return this.name  }}// 在 interface 中只能存在类型声明interface PersonalIntl {  name: string  sayHi (): string}

In some cases interface , there is no difference between the use and the normal type definition.
For example, we want to export an name object that exists and has age two attributes:

// types/personal.d.tsexport interface PersonalIntl {  name: string  age:  number}// index.d.tsimport { PersonalIntl } from './types/personal'const personal: PersonalIntl = {  name: 'Niko',  age:  18,}

If you are going to interface change type the definition, it is perfectly fine:

// types/personal.d.tsexport type PersonalIntl = {  name: string  age:  number}

Such a definition is completely no problem based on the use of the top, but it is only applicable to the Object literal declaration, there is no way to use a good constraint class mode, so we use interface to constrain class the implementation:

import { PersonalIntl } from './types/personal'class Personal implements PersonalIntl {  constructor(public name: string, public age: number) { }  // 上边的简写与下述代码效果一致  public name: string  public age: number  constructor (name: string, age: number) {    this.name = name    this.age = age  }}const personal = new Personal('niko', 18)
Some doubts about the function member declaration

First, there are two ways to define a function in an interface, one is defined on the instance and one is defined on the prototype chain.
The two ways of declaring this are as follows:

interface PersonalIntl {  func1 (): any      // 实例属性  func2: () => any   // 原型链属性}

But when we implement these two properties we can actually convert to each other, and there is no strong requirement to use which way:

class Personal implements PersonalIntl {  func1 () {    console.log(this)  }  func2 = () => {    console.log(this)  }}

In fact, the two are different in the compiled JavaScript code, it is not clear whether this is a design is the bug case, similar to the structure:

var Personal = /** @class */ (function () {    function Personal() {        var _this = this;        this.func2 = function () {            console.log(_this);        };    }    Personal.prototype.func1 = function () {        console.log(this);    };    return Personal;}());

Therefore, it is advisable interface to create in a defined way, avoiding some strange and bizarre problems that may exist.

Automatic merging of interface declarations

Because interface it is TypeScript unique, there will be some interesting features, such as the same name interface will be automatically merged:

interface PersonalIntl {  name: string}interface PersonalIntl {  age: number}class Personal implements PersonalIntl {  name = 'Niko'  age = 18}
Do not use function overloading in interface

interfaceusing a function overload in, you get an incorrect result, or the upper build function, if interface declared in, then implemented in, class then whatever the call, the type of the return value will be considered any .

So the correct approach is to class declare the overloads in, class implemented in, with interface a maximum of only one definition any , not three overloads.

class Util implements UtilIntl {  build(arg: Date): number  build(arg: Object): string  build(arg: any): any  build(arg: any) {    if (arg instanceof Date) {      return arg.valueOf()    } else if (typeof arg === 'object') {      return Object.prototype.toString.call(arg)    } else {      return arg    }  }}
Summary

TypeScriptrelated to declaring type declarations are currently summarized in these more commonly used, welcome to the small partners to complement.

There is a definition of existence and in the previous version module namespace , but for the time being, it seems to be more recommended to use the Es-modules version import / export to implement a similar function rather than a custom syntax, so we skipped over the two keyword-related descriptions

There are templates in the official documentation for how to write a declaration file, which can be referenced in: Transfer matrix

Resources
    • Keyof
    • Record
    • ReturnType and other built-in functions

How to write a Typescript declaration file

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.