Angular2 manages external type definitions and handles “duplicate identifier” typescript errors

Time:2020-10-31

When learning angular2, according toAngular2ProvidedquickstartAs a seed project. After installing all packages and running the project, a full screen compilation error occurred

error TS2300: Duplicate identifier

Let’s talk about the cause of this error and how to fix it. To better understand this, let’s talk about the differences between typescript and JavaScript, as well as between external type definitions and @ types.

introduce

Before I dive in, let’s talk about the external type definition of typescript.
If you are familiar with the difference between typescript and JavaScript, you can skip this section.

Typescript is a statically typed language, a superset of JavaScript. Static typing means that the program we write has to go through a compilation stage, in which the compiler performs type checking to verify the correctness to a certain extent. For example:

// person-human.ts

class Person {
  talk() {
    // ...
  }
}

class Superhero extends Person {
  fly() {
    // ...
  }
}

let bar = new Person();

bar.fly();

If we compile the above code, the typescript compiler will report an error:

$ tsc person-human.ts
Property 'fly' does not exists on type 'Person'.

If we run the corresponding JavaScript file, we get an error:

$ node person-human.js
Uncaught TypeError: bar.fly is not a function

Although there are two kinds of errors in TSCCompile timeError reported, and node is inRunning timeWrong report.

Compile time errors are easily handled by developers because they are based on the developer’s static code analysis. This means that the compiler can notify the developer of possible errors before the user uses the code.

On the other hand, we use JavaScript dynamic typing, and we can’t find all the mistakes we make. Static code analysis for dynamically typed language code is very difficult (almost impossible), so it is quite possible that after the code is released to the production environment, there are still no possible problems.

External type definition

Since typescript is a superset of JavaScript, we want to use typescript code and JavaScript code together. For example, we want to use jQuery in angular2 (a framework written in typescript).

The solution is: typescript is compiled into JavaScript code, which can communicate with JavaScript code after compilation, which is very good. But how to use jQuery in typescript code? Take a look at the following example:

// jquery-demo.ts
let a = $('.foo');

We call the $function selector. The above code will generate the following compilation error:

$ tsc jquery-demo.ts
Cannot find name '$'.

This is because typescript is stricter than JavaScript, and it cannot assume that a jQuery reference may be included in our page and depends on it. Typescript needs to have a $Statement

This is why external type definitions appear. To declare, we have a global function called $that we can do this:

// jquery-definition.ts
declare var $: Function;

let a = $('.foo');

We run again:

$ tsc jquery-definition.ts

# Will output the file jquery-definition.js

Note: we just declared a $function, not a $function. If we run jQuery- definition.js We still get a run error because $is undefined.

Why use external type definitions

The main purpose of external type definition is to allow text editors and IDE to statically analyze packages and frameworks written by JavaScript. This is very beneficial because we can compile errors and be intelligent at development time. It can reduce errors caused by misspelling property names or passing wrong objects to functions at run time.

Managing external type definitions

Packages written in JavaScript can use external type definitions, such as jQuery, angularjs 1. X, react, and so on.

A few years ago,DefinitelyTypedA cli (command line interface) manager called TSD was created

Later, the tool was abandoned and more advancedtypingsReplace. Typing allows us to download the external type definition of the package we are using

As part of the typescript 2 release, MicrosoftA method was announced, use NPM registration to manage the type definition outside typescript

stay tsconfig.json In compilerpoptions, contains a lib attribute. It contains a collection of library files and the corresponding type definition of library. This means that if your compilation target is Es5 and building for browsers, then you should include Es5 and DOM, if you want to use the features of ES6, you should include ES6, etc. For more information about the Lib attribute referencehere
Install external type definitions for jquery

$ npm i @types/jquery --save-dev

Note: Using — save dev is more appropriate than — save, because you don’t want users of your NPM package to install your third-party dependent type definitions

Use external type definitions

How to use the provided type definition?

In the directory where we installed jQuery external type definition, create the following file:

// jquery-demo.ts

/// <reference path="./node_modules/@types/jquery/index.d.ts"/>
let height = $('.foo').height();

It works, but it’s quite inflexible. If we change the path of the defined file or other defined file paths, we need to change the value of the path attribute. tsconfig.json It provides us with a flexible feature:

tsconfig.json

tsconfig.json Is the configuration file defined by typescript in the tsconfig.json You can configure the typescript compiler. Here is a column:

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "es6"],
    "typeRoots": [
      "./node_modules/@types"
    ]
  },
  "exclude": [
    "node_modules",
    "dist"
  ],
  "compileOnSave": false
}

To tell TSC where to find external type definitions for installation, you can use the typeroots array, which is a property of compilerpoptions.

In the configuration file, use Es5 as the target language and exclude node_ Modules and dist directories.

In order to have the typing of ES6 APIs, you need to install @ types / core JS:

$ npm i @types/core-js

Finally, our directory structure is as follows:

.
├── jquery-demo.js
├── jquery-demo.ts
├── tsconfig.json
└── node_modules
    └── @types
        ├── jquery
        │   ├── package.json
        │   │── types-metadata.json
        │   └── index.d.ts
        └── core-js
            ├── package.json
            │── types-metadata.json
            └── index.d.ts

because tsconfig.json We can compile jQuery with the following command- demo.ts Document:

$ tsc

As a result, a lot of mistakes were made

./../.npm-packages/lib/node_modules/typescript/lib/lib.es2015.core.d.ts(17,14): error TS2300: Duplicate identifier 'PropertyKey'.
node_modules/@types/core-js/index.d.ts(21,14): error TS2300: Duplicate identifier 'PropertyKey'.
node_modules/@types/core-js/index.d.ts(85,5): error TS2687: All declarations of 'name' must have identical modifiers.
node_modules/@types/core-js/index.d.ts(145,5): error TS2403: Subsequent variable declarations must have the same type.  Variable '[Symbol.unscopables]' must be of type '{ copyWithin: boolean; entries: boolean; fill: boolean; find: boolean; findIndex: boolean; keys: ...', but here has type 'any'.
node_modules/@types/core-js/index.d.ts(262,5): error TS2687: All declarations of 'flags' must have identical modifiers.
...

This is caused by multiple versions of the same type definition. stay tsconfig.json The Lib attribute contains ES6. At the same time in node_ There are also core JS modules in modules / @ types. To fix this problem, there are two options:

  • In the Lib property of compiler options, change ES6 to Es5. This method typescript cannot contain ES6 type definition

  • From node_ Delete the core JS module from modules. In this way, typescript can only use internal ES6 type definitions

Because the ES6 type definition that comes with typescript is more reliable than that of a third party, delete the node_ Modules / @ types / core JS

$ rm -rf node_modules/@types/core-js

If we run TSC again, we will get the compiled jQuery- demo.js file

Even if we start from jQuery- demo.ts Deleting the < reference / > tag is still running normally. TSC’s behavior is: “takeall the files and all the type definitions from the current directory and all of its subdirectories, except the ones declared in the exclude array”That means we can, toouse tsconfig.json The exclude property of the

tsconfig.json There is also the files property, which, if set, takes the value of TSC into account the files contained in the files.

There is no type definition for a namespace

You may be wondering why compiler options lib and core JS have the same type definition?Typescript external type definitions cannot use a namespace。 Why not? If you use jQuery in your project, you can’t include two different sets of external type definitions because there is only a single jQuery global variable

Reference link:
http://blog.mgechev.com/2016/…
http://stackoverflow.com/ques…