Search code examples
javascriptjavatypescriptconventions

TypeScript export-import conventions


In contrast to Java (and other languages), TypeScript offers you multiple ways to export and import things (classes, functions etc.).
For example, you can export multiple classes, constants, functions etc. per file.
Also you can define 1 default export per file.
On the other side you can import only the default export, import everything using an alias or import the given things.
Coming from Java, I wonder, if there is any convention, especially regarding constants and functions.
Let's say I have a util-file, having a lot of functions. In Java I would create a File Util.java with class Util, which contains all the static functions.
In TypeScript I have following possibilities:

  1. Export every single function and import it using import * as Util.
  2. Create a class Util with static functions and export only this class.

In both cases I can call functions using Util.functionName(), just like in java.

Another case is a class with a few constants.
For example I have a class Car with a field type. There are also constants for the available types, like TYPE_SUV, TYPE_SPORT and so on.

Again, I can define them as "top-level" constants and export them and the class Car. But I can also define them as public static readonly inside the Car-class and export that class only.
Using the first approach, I would have a verry big import statement, if I need all constants in other files. Also sub-classes won't "inherit" those constants.
However using readonly instead of const feels kind of strange to me...

So I was looking for some kind of convention, but I did not find much.
I only found a few tips like this.

So, are there any guidelines regarding exports and imports in TypeScript, which take care about the explained problems? I am also looking for the best way, regarding integration of tools (refactoring, auto-importing, organizing imports etc.).

Thanks.


Solution

  • (I am afraid this is going to be somewhat opinion based although there are objective elements in choosing one over another)

    1. Export every single function and import it using import * as Util.
    2. Create a class Util with static functions and export only this class.

    I prefer number 1) because 2) does not bring any benefit and may lead to someone instantiating the Util class for no reason (and contrarily to Java, this cannot be prevented).

    I can define them as "top-level" constants and export them and the class Car. But I can also define them as public static readonly inside the Car-class and export that class only.

    With regards to constants, the readonly modifier only applies at compile time. So as class properties, they can still be modified at runtime. I, hence, prefer to make them top level constants in the module (file)

    The excellent handbook from Basarat is also my "reference" and I share his opinion on default exports which do not bring much aside from confusion since with named exports, it is also possible to rename on import e.g.

    import { OriginalName as MyPreferredName}  from './OriginalName'