By: Kazehaiya

Original text: kazehaiya. Dead simple. IO / 2019/07/07 /…

preface

Due to the recent TS migration of the project, as the first person to eat crab, I stepped on many pits. Most of the problems encountered during the migration were resolved, but there was no explanation for the naming of the shims-vue.d.ts file and the module declarations within it. Sink down to read some external network information, finally is a little “clear clouds see sky” feeling. Here are some of my thoughts on the TS global module declaration and some of the pitfalls encountered by the TS project migration.

Vue TS declaration file

After installing @vue/typescript, the project generates two new files, shims-vue.d.ts and shims-jsx.d.ts, which say:

// shims-vue.d.ts

declare module '*.vue' {
  import Vue from 'vue';
  export default Vue;
}
Copy the code

and

import Vue, { VNode } from 'vue';

declare global {
  namespace JSX {
    // tslint:disable no-empty-interface
    interface Element extends VNode { }
    // tslint:disable no-empty-interface
    interface ElementClass extends Vue { }
    interface IntrinsicElements {
      [elem: string] :any}}}Copy the code

So what do these two documents do?

shims-vue.d.ts

The Ambient Declarations are for all vue files in the project. Ts only recognizes.d.ts,.ts, and.tsx files by default. (Even if the module declaration of Vue is added, the IDE will not recognize the.vue ending file, which is why the suffix must be added when importing Vue files, and no errors will be reported without adding the build.)

shims-jsx.d.ts

The latter is the global namespace of the JSX syntax, because value-based elements are simply looked up by their identifiers in the scope they are in (defined here using the method of ** stateless function component (SFC)**). When JSX syntax support is enabled in tsconfig, It will automatically recognize the corresponding.tsx ending file, please refer to JSX website.

Problems arising

First of all, the official documentation does not refer to Shims-XXx. d.ts as a general template. It only gives us the following template examples:

  • global-modifying-module.d.ts
  • global-plugin.d.ts
  • global.d.ts
  • module-class.d.ts
  • module-function.d.ts
  • module-plugin.d.ts
  • module.d.ts

So what do you make of these two files?

Can changes be made in uniform specification files?

How are global interface, namespace, module declarations defined? How should I write it?

. For all these problems, the following are analyzed in turn.

To reassure

Understand and modify SHIMS-XXX.D. ts

As we know, the xxx.D. ts file indicates that some of its internal declarations are global and can be retrieved within project components. So the two shims-XXX.d.ts generated by Vue are actually meant to indicate that the two files are VUe-related global declaration files.

However, from a project management perspective, as more NPM modules are introduced (such as packages with no types on internal NPM sources), there will be more and more external declaration files that mimic Vue’s declaration file style, and folders will not look very comfortable. So is there a better way to solve the problem of too many files?

As for me, I prefer to keep these simple declarations in a.d.ts file. The website also recommends maintaining them in a large module, so we can maintain a module.d.ts to declare all external modules in general. Based on the official example, I made two files to manage declarations for external modules, module.d.ts and declarations. The former mainly maintains the more detailed external modules that need to be written, while the latter mainly maintains the simplified mode modules (including internal.js files that need to be declared, compatible with historical legacy issues). Such as:

Modified Module /index.d.ts

// This `declare module` is called ambient module, which is used to describe modules written in JavaScript.

// Add vUE plugin declaration for VUe-clipboard2
declare module 'vue-clipboard2' {
  import { PluginFunction } from 'vue';
  const clipboard: PluginFunction<any>;
  // Define the default export type
  export default clipboard;
}

// Add the fe-monitor-SDK Vue plugin declaration
declare module 'fe-monitor-sdk' {
  import { PluginObject } from 'vue';
  // Define destruct variable types
  export const monitorVue: PluginObject<any>;
}

// Add declarations for all.vue files
declare module '*.vue' {
  import Vue from 'vue';
  export default Vue;
}
Copy the code

Modified module/declarations. D. ts

// Shorthand ambient modules, All imports from this shorthand module will have the any type.

declare module '@/cookie-set';
Copy the code

Additional: Declare visibility categories for global, such as general global.d.ts, and other visibility categories (if there is a large number of types) according to the corresponding type, such as table can be all in global-table.d.ts.

How to write a global declaration

Another is always doubt the problem is that global declarations of writing, such as the module of “single file module declaration” writing “single file module consolidated statement” writing, “global declarations without import file” and “global declarations with import declaration documents” of writing was somewhat different, here I list the feasible writing as well as the different reasons.

Note: some definitions here are personal summary of easy to remember, for non-standard definitions.

Single file single module declaration

The file can be written in two ways, as follows:

/ / write one
declare module '*.vue' {
  import Vue from 'vue';
  export default Vue;
}

/ / write two
import Vue from 'vue';

declare module '*.vue' {
  export default Vue;
}
Copy the code

Note: The former (notation 1) mainly adds the types declaration for modules that do not have TS declaration, while the latter (notation 2) mainly extends the types declaration for modules that already have TYPES declaration (refer to vue-router source code section).

Single-file multi-module merge declaration

There is only one way to write this (you need to turn off the corresponding multiple-entry duplicate module lint rule or ignore everything in this types folder)

declare module '*.vue' {
  import Vue from 'vue';
  export default Vue;
}
Copy the code
No imported global declaration file

No import means that there is no import declaration and global interfaces and functions are defined directly

interface TableRenderParam extends BasicObject {
  row: BasicObject,
  key: string, index? :number,}Copy the code
Global declaration file with import declaration

An import plug-in declaration with import must display the definition global, for example:

import { CreateElement } from 'vue';

/ / function
declare global {
  interface TableRenderFunc {
    (h: CreateElement, { row, key, index }: TableRenderParam): JSX.Element,
  }
}

/ / namespace
declare global {}
Copy the code
Different reasons

If the TS error message is displayed when the import is raised to the top of the “Single-file multi-module merge statement”, indicating that the module cannot be further expanded, why is the error message displayed after the import is raised indicating that the module cannot be further expanded?

Personal research has concluded that when import is pushed out of a module, it indicates that other declare modules in the file are already ts declared modules, Declare the module to extend its original declaration (see vue-router extension for vUE). However, for modules that do not have a TS declaration, we cannot get the TS declaration, so we did not send module extension, so we reported an error.

When you put import inside a module, because a module identifies itself as a module, it can be used as a module declaration to add declarations to modules that do not have corresponding declarations.

In addition, for multiple declare global, the declaration merges the declaration so that all module declarations are merged into the same global declaration. Therefore, for global declaration files with import declarations that carry import declarations to the outer layer, Split file global maintenance or single file declaration merge maintenance is possible.

Note: In TypeScript, as in ECMAScript 2015, any file that contains a top-level import or export is treated as a module. Conversely, if a file does not have a top-level import or export declaration, its contents are considered globally visible (and therefore visible to the module).

Remaining TS issues with project migration

Of course, there are many problems encountered in the process of project migration, as a side item for your reference.

Dynamically introduce files without TS declarations

Because the dynamic setting of cookies will vary with the test machine, and different people develop, the cookie will also change, so you need to clear this file git trace and dynamic import (online not in), at the same time support.js/ts declaration.

The original writing:

// Determine the current environment in the cookie-set file
import '@/cookie-set';
Copy the code

Change one: Remove git trace and present environment judgment

/ / git parts
git rm --cache <cookie-set file path>

// The file part uses dynamic import
if (process.env,NODE_ENV === 'development') {
  import('@/cookie-set');
}
Copy the code

Because dynamic import requires ts declarations, there are no trace files. To support.js files, add simple declarations in declarations

declare module '@/cookie-set';
Copy the code

Imported plug-ins do not have vUE plug-in declaration

In order to facilitate everyone’s understanding, I will warm the heart to paste the code again, pay attention to see the change after the comment ~

// This applies to import vueClipboard from 'vue-clipboard2';
declare module 'vue-clipboard2' {
  import { PluginFunction } from 'vue';
  const clipboard: PluginFunction<any>;
  export default clipboard;
}

// This applies to import {monitorVue} from 'fe-monitor-sdk';
declare module 'fe-monitor-sdk' {
  import { PluginObject } from 'vue';
  export const monitorVue: PluginObject<any>;
}
Copy the code

Export and Export default can be found in the module section

The vue-router reference path is faulty. Procedure

Although alias is configured in Webpack, it is only used when webpack is packaged, TS does not recognize it, it has its own configuration file, so we need to configure two more places to solve this problem. The tsconfig.json path needs to be configured first

// tsconfig.json
path: [
  "@ / *": [
    "src/*"].// ...
]
Copy the code

The other is that references to vue files must be added with the.vue suffix, because the editor makes it impossible to recognize the.vue suffix, so all references to vue files need to be added with the.vue suffix.

Vue mixins file writing method

Refer to the TS vUE introduction documentation and modify it as follows

// The original way of writing
export default {/ * * /}

// The current way of writing
import Vue form 'vue';
export default Vue.extend({/ * * /})
Copy the code

Note that for computed in this section, you need to add a return value type, otherwise an error will be reported

Declaration of the data section

Data is a function, and its object is a return value. It is not written in Class style, so the declaration of this part should read as follows:

data(): Your Interface here {
  return {};
}

/ / or
data() {
  return <Your assertions here> {};
}
Copy the code

VS Code experimentalDecorators problem

To do this, add the following properties to tsconfig.json:

"experimentalDecorators": true
Copy the code

Because the decorator is an experimental feature in its current version and may change in future releases, you need to configure this parameter to remove warnings.

Class static methods

Abstruct abstract class is generally used to standardize the details of classes such as methods and attributes. However, the static part of “class” cannot be abstracted and needs to be processed separately in the corresponding static method part. There is some doubt as to whether there is a good way to handle this part (i.e. to extract a declaration such as interface) 🤔. This problem was left at the beginning of the development of the current thought of more reliable writing method has two.

The namespace writing

It is also stated in the official documentation that namespace is recommended for global naming of modules in the business, so namespace can be used for common public methods in the business.

Written for multiple namespaces, available aliases written import NS = FirstNameSpace. SecondNameSpace, then directly through NS. XXX to take corresponding properties can directly. Also different from import someModule = require(‘moduleName’) used when loading modules, where the alias is simply created to simplify code.

The module file

Another idea that can be used in ES6 is import + export. Because there are only static methods in a class, we can think of this class as a module, and each module corresponds to a file. Therefore, we can store corresponding methods as a TS file, and import them in import if necessary.

The specification of DefinitelyTyped
  • If your module needs to introduce new names into the global namespace, you should use global declarations.
  • If your module does not need to introduce new names into the global namespace, then you should use the module export declaration.

Expand the content

namespace

Namespace in TS mainly solves the problem of naming conflicts. It generates an object globally, and all classes defined inside a namespace are accessed through the attributes of this object. For internal modules, try to use namespace instead of module, refer to the official documentation. Such as:

namespace Test {
  export const USER_NAME = 'test name';

  export namespace Polygons {
    export class Triangle { }
    export class Square { }
  }
}

/ / take alias
import polygons = Test.Polygons;
const username = Test.username
Copy the code

Import xx = require(‘xx’) import xx = require(‘xx’)

The default namespace of the global environment is Global

module

A module can be understood as a single Vue file in a Vue, which is divided into functional units, with one module responsible for one function. The biggest difference between namespace and namespace is that namespace is cross-file, module is a file as a unit, a file corresponds to a Module. By analogy with Java, a namespace is like a package in Java, and a Module is like a file.

Reference documentation

  • Namespaces and modules
  • Clarify what “ambient” means
  • Ambient Declarations
  • TypeScript tutorial
  • Typescript: IDE reports TS2307
  • typescript declare third party modules
  • The differences and fundamental implications of TypeScript’s two declarative file writing styles
  • Distinguish namespace and Module in TS