Typescript - how to use the utility file on a project-wide scale with minimal ceremony?

I have been working with Typecipt for several months, and this aspect has already annoyed me during several projects. Suppose I have the following project structure:

project +- app | +- component1 | | +- component1.ts | +- component2 | | +- component2.ts | +- utils | +- utils.ts +- tsconfig.json 

Now suppose the utils.ts file contains many helper classes (for example, data structures) and possibly even global functions, and almost every other file in the project uses at least part of it. How would you use it with a minimal amount of template code?

Using the import statement

 // utils.ts export function foo() { return 1; } // component1.ts import { foo, bar, baz, ...(a long list here) } from '../utils/utils' let x = foo(); // looks fine 

Pros: Imported items are easy to use.

Cons: having to maintain a long import list in almost every other file

Another option is to import everything at once:

 // component1.ts import * as utils from '../utils/utils' // I have to remember to put this line on top of every other file, but it better than the option above let x = utils.foo(); // unavoidable prefixes all over the code 

Pros: the import expression is generic and can be copied to each file only by changing the relative path (although I would be happier if there was a way to do this implicitly throughout the project)

Cons: namespace prefixes for simple helper functions throughout the code. For some reason, Typescript does not allow import * to be used without an explicit namespace.

Using the triple-slash directive

 // utils.ts function foo() { return 1; } // no export keyword // component1.ts /// <reference path="../utils/utils.ts" /> let x = foo(); 

It looks much better, although I still need to provide a link with a relative path on top of each file. However, it completely breaks for me when I try to link my application with webpack - utils.js is simply not included with the rest of the application (I think this is the topic for another SO question).

I would really like the opportunity to use my code in utils.ts, for example, I use the classes of the global library - Map , Set , HTMLElement , Promise , etc. without any import or prefixes, is there any way to achieve this?

PS Please note that I have long abandoned the good principle of programming, actually breaking my utils.ts into several files (one per class / global function), as this will greatly aggravate the problem above. I am really considering using one file for the entire project already.

+5
source share
1 answer

How to add utils to global scope

When you do /// <reference path="../utils/utils.ts" /> , what you tell the Typescript compiler is "everything in the utils.ts module is in global scope." Typescript will not do anything to make this happen, you just tell TS that it is done, and he believes you. (For example, if you loaded a library with the <script src=".."> in your HTML to load the library worldwide, you should use this annotation to tell Typescript about it.)

If you want to go this route, you will need an annotation to tell Typescript that the utils are in the global scope, but you will also need to actually add logic to make this happen. Webpack has a number of ways to define global variables , but one option would be to use global , with code like this somewhere at the beginning of your application:

 import * as utils from "../path/to/utils"; for(const key in utils) { global[key] = utils[key]; } 

Now all your utils are on a global scale, in reality, and link link annotation tells Typescript that Typescript is happy.

Why I did not add utils to the global scope

But honestly, I can’t say that I recommend doing this in practice. Global area pollution is a fairly classic anti-pattern and, as a rule, programs are difficult to execute. Anyone reading your code will simply see “magic” functions, such as foo , that seem apparent from nowhere, without indicating where the function is from. And, like all additions to global reach, it opens the door to name clashes.

Personally, I recommend just performing standard import operations: personally, I tend to import { foo } from "utils/utils" , although sometimes utils.foo easier to encrypt (for example, if between the imported module that I import, name collision between some other foo in )

Some import guidelines are less painful:

Do not use relative import paths.

With a little configuration for TS and webpack you can write your import, for example

 import * as utils from 'utils/utils' 

Instead:

 import * as utils from '../../utils/utils' 

The relevant configuration bits are: "include" in your tsconfig and "resolve.modules" inside your webpack configuration. (Or resolve.root if you are using webpack 1.0). In your case, you want to add an “application” to both of these lists to indicate that the “application” is the root of your source files.

Code snippets for commonly used imports

When using sequential import paths, if you have many frequently used imports, you can create a code snippet for them: some code that you can quickly insert by typing a specific shortcut. (They are called fragments in VSCode, Atom, and SublimeText, I believe other editors have similar functions)

For your hypothetical mathematical application (from the comments on the question) you can create a snippet for

 import Vector from "path/to/Vector"; import Matrix from "path/to/Matrix"; import Tensor from "path/to/Tensor"; 

to quickly import all these things when creating a new file.

VSCode now supports auto-import

You can type foo and press enter, and it will automatically add the import for foo to the top of your file. I don’t like sewing for a specific editor, but if you are annoyed by the hard work of importing, it is really useful.

Shorter namespaces

If you're using namespace imports, maybe just choose a shorter one: u.foo is just a little longer than just foo , but it is much less magical than a monkey with a global scope.

+1
source

Source: https://habr.com/ru/post/1270810/


All Articles