<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sergey Nikitin</title>
    <description>The latest articles on DEV Community by Sergey Nikitin (@n0th1ng_else).</description>
    <link>https://dev.to/n0th1ng_else</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F597844%2F22fba514-fd94-4c54-840b-ba95a808b9f0.jpeg</url>
      <title>DEV Community: Sergey Nikitin</title>
      <link>https://dev.to/n0th1ng_else</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/n0th1ng_else"/>
    <language>en</language>
    <item>
      <title>Adapting One Old UI Components Library To Work In TypeScript Code</title>
      <dc:creator>Sergey Nikitin</dc:creator>
      <pubDate>Sun, 08 Aug 2021 19:48:18 +0000</pubDate>
      <link>https://dev.to/n0th1ng_else/adapting-one-old-ui-components-library-to-work-in-typescript-code-286o</link>
      <guid>https://dev.to/n0th1ng_else/adapting-one-old-ui-components-library-to-work-in-typescript-code-286o</guid>
      <description>&lt;p&gt;THE first public version of TypeScript appeared more than &lt;a href="https://devblogs.microsoft.com/typescript/announcing-typescript-1-0"&gt;7 years ago&lt;/a&gt;. Since that time it grew up and brought many incredible features for developers. Today it slowly becomes a standard in the JavaScript world. Slack, AirBnB, Lyft, and many others add TypeScript into their tech stack. Teams use TypeScript for both browser applications and NodeJS services. There are always pros and cons to this decision. One disadvantage is that many NPM packages are still written as JavaScript modules. We experienced this issue as well when decided to migrate our applications to TypeScript. We had to implement type definitions for our internal UI components library. We wanted to get a tool, that could serve developers as additional documentation. We also wanted to collect everything engineers can use while working with the JS library, in one place. I am going to tell you what steps did we take to achieve the desired solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Type definitions
&lt;/h3&gt;

&lt;p&gt;You can describe all data that are being exported by a particular JavaScript module. The TypeScript analyzer will pick it up and will handle the package in a way you defined it in the type definitions file. The approach is close to C/C++ declaration files. Here is a simple example, imagine you have a trivial JS module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sample.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pageSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pageSizes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getOffset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
one simple `sample.js` module might look like this





&lt;p&gt;You can use the &lt;code&gt;sample.js&lt;/code&gt; module in TypeScript code without any problems. But guess what? The analyzer would not be able to run autocomplete and infer types properly. If we want to rely on help from smart tools, we need to manually describe the API provided by our JS module. Usually, it is pretty straightforward to do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sample.d.ts&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pageSizes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getOffset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
standard way to declare types for TypeScript is to create an appropriate `.d.ts` module





&lt;p&gt;Note that definition files have priority over JavaScript modules. Imagine you removed &lt;code&gt;export const pageSizes = [25, 50, 100]&lt;/code&gt; from the &lt;code&gt;sample.js&lt;/code&gt; module. TypeScript would still think it exists, and you will get a runtime error. It is a known tradeoff to keep definition files in sync with real JavaScript code. Teams try to update type definitions as soon as possible to provide a smooth experience for other developers. In the meantime, this approach allowed the TypeScript codebase to raise gradually without having to rewrite the whole JavaScript ecosystem.&lt;/p&gt;

&lt;p&gt;There are many examples of how to write type definitions. Most of the time you will meet simple cases and thus would be able to find something similar in the repository called &lt;a href="https://github.com/DefinitelyTyped/DefinitelyTyped"&gt;DefinitelyTyped&lt;/a&gt;, where developers store definitions for NPM packages. You can also learn more about the type definitions feature in the &lt;a href="https://www.typescriptlang.org/docs/handbook/2/type-declarations.html"&gt;official documentation&lt;/a&gt;. It is not a part of this article.&lt;/p&gt;

&lt;h3&gt;
  
  
  Our JavaScript library
&lt;/h3&gt;

&lt;p&gt;In our company, we develop an internal UI components library. We use it in our products from the beginning, and the current production version is 12. You could only imagine how much effort it would take to rewrite such a big thing. In the meantime, we write new features using the TypeScript language. The problem is, every time one team goes to implement a new code, they write a small copy of the UI library definitions. Well, this does not sound like a good process, and we decided to have a separate package with complete type definitions for our UI components. Key points here are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We would be able to import this package during the new repository initialization. This will allow controlling the version and simplify the refactoring during the version update.&lt;/li&gt;
&lt;li&gt;We would stop copy-pasting the same code again and again.&lt;/li&gt;
&lt;li&gt;Type definitions is a great documentation source. I bet developers would prefer to select the method from &lt;strong&gt;IntelliSense&lt;/strong&gt; suggestions rather than go to the web page with all API descriptions and copy the method name.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  So what is wrong?
&lt;/h3&gt;

&lt;p&gt;Now you may ask me, what is wrong with our library? The thing is that we inject some global variable to interact with the exposed API. In addition, we want to import some constant pre-defined values (icons, table cell types, tag colors, etc.) that can be used by the UI components. They usually come in form of constant identifiers that help to style components.&lt;/p&gt;

&lt;p&gt;For example, we can style a button with one of the types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// lists/button.ts&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;ButtonType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Primary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ui-primary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Secondary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ui-secondary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Danger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ui-danger&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
depending on the value, the button will be rendered in a specific size and color palette





&lt;p&gt;We came to an idea to store all library-specific values in one place. So this project became not just type definitions for the UI library, but a real package! It should represent the exact library state at some specific version. And this is interesting - how can we implement this? Let's state what we want to achieve as the result:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We want the global variable &lt;code&gt;ui&lt;/code&gt; to be accessible without having to import anything.&lt;/li&gt;
&lt;li&gt;We want our UI components definitions to be available without having to import anything as well.&lt;/li&gt;
&lt;li&gt;We want to use predefined constants and objects for UI components by importing them from our types package. There should not be any conflict to assign some type from the library in this case.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sounds like a small deal, right? Let's write some &lt;code&gt;.d.ts&lt;/code&gt; file with type definitions and... Oh, wait, you can't put real code (constants, enumerable lists, and other stuff) in the &lt;code&gt;.d.ts&lt;/code&gt; file! Sounds reasonable. Let's create a regular &lt;code&gt;.ts&lt;/code&gt; file and put all these enums there. Then we... well, how can we apply globals in the &lt;code&gt;.ts&lt;/code&gt; file?! Meh...&lt;/p&gt;

&lt;p&gt;We did not find an example of how to do that, really. StackOverflow is flooded with the &lt;code&gt;.d.ts vs .ts&lt;/code&gt; concept war. We had nothing but digging into TypeScript documentation and finally introduced the code that meets our requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Start from the scratch
&lt;/h3&gt;

&lt;p&gt;First things first. We write interfaces and enums as usual. I am going to provide code examples in a simplified matter, so we would focus on the approach, not the particular code features. Imagine we have a notification dialog, so we write something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// interfaces/notification.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ButtonType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../lists/button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;NotificationButtonConfig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ButtonType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Notification&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buttons&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;NotificationButtonConfig&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buttons&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;NotificationButtonConfig&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buttons&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;NotificationButtonConfig&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
simple notification API allows assigning a text message and buttons





&lt;p&gt;Where &lt;code&gt;ButtonType&lt;/code&gt; values are from enum we saw already:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// lists/button.ts&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;ButtonType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Primary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ui-primary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Secondary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ui-secondary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Danger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ui-danger&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
we highlight a button according to the type





&lt;p&gt;Then let's take a look at the simple case. We don't import anything, as the UI components expose the global variable, and we want to call a notification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// example/application/moduleNoImport.ts&lt;/span&gt;

&lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Document has been saved!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
we call a global API to show the notification dialog without custom button configuration





&lt;p&gt;What do we need to do to make it available? We are going to enrich the &lt;strong&gt;global&lt;/strong&gt; namespace with the &lt;code&gt;ui&lt;/code&gt; variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UiLib&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./interfaces/ui&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UiLib&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
we simply add a new variable into the global namespace





&lt;p&gt;&lt;code&gt;UiLib&lt;/code&gt; here describes everything our UI library exposes into the global scope. In our example, we have a list of methods that show different kinds of notifications:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// interfaces/ui.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Notification&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./notification&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;UiLib&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Notification&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
all the notifications API is collected under the Notification interface





&lt;p&gt;This is almost it. Lastly, we adjust the package configuration. We tell TypeScript to emit type declarations by adjusting the &lt;code&gt;tsconfig.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"declaration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"declarationDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/es"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
always emit declaration files





&lt;p&gt;We now control how TypeScript emits the output. We also specify a path to our types in &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/es/index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/index.d.ts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
don't forget to set up a build step for your package to compile TypeScript files





&lt;p&gt;Alright, then we install the package in our project. Finally, we specify the package path in the project's &lt;code&gt;tsconfig.json&lt;/code&gt; (since we don't use the default &lt;code&gt;@types&lt;/code&gt; folder) to see that it works!&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the values
&lt;/h3&gt;

&lt;p&gt;Now let's go deeper. What if we want to create a notification with some specific button? We want to be able to write something similar to this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// example/application/moduleWithImport.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UiCore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ui-types-package&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;showNotification&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sad!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UiCore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ButtonType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Danger&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
we want to show notifications with the button of Danger type





&lt;p&gt;Note here and below &lt;strong&gt;UiCore&lt;/strong&gt; is a namespace that contains all the enums, configs, interfaces our UI library operates with. I think it is a good idea to collect everything under some namespace, so you would not think of names for each interface. For instance, we have a &lt;code&gt;Notification&lt;/code&gt; interface. It sounds quite abstract, and it takes a while to understand the exact object behind the naming. In the meantime &lt;code&gt;UiCore.Notification&lt;/code&gt; clearly describes where it comes from. Having a namespace is just an optional but convenient way to handle such things.&lt;/p&gt;

&lt;p&gt;Right now we can't import &lt;code&gt;UiCore&lt;/code&gt; from the library as we don't export anything. Let's improve our code and form the namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// namespaces/core.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;notificationInterfaces&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../interfaces/notification&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;buttonLists&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../lists/button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nx"&gt;UiCore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;NotificationButtonConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notificationInterfaces&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NotificationButtonConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ButtonType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;buttonLists&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ButtonType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
we use composite export to create an alias for objects under the namespace





&lt;p&gt;We basically export all data we have under the namespace with &lt;code&gt;export import&lt;/code&gt; alias syntax. And, since the main package module is &lt;code&gt;index.ts&lt;/code&gt; in the root, we write a global export to expose the namespace to the public:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UiLib&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./interfaces/ui&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UiCore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./namespaces/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UiLib&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
we export UiCore, and now it is available from the outside





&lt;p&gt;Two simple steps to achieve our goal! Now we can import some enum and enjoy writing the code. OR. Or we can think of some other use cases. In the example above, we used the &lt;code&gt;ButtonType.Danger&lt;/code&gt; value to create a notification with some pre-defined button. What if we want to use &lt;code&gt;ButtonType&lt;/code&gt; as a parameter type?&lt;/p&gt;

&lt;h3&gt;
  
  
  Covering edge cases
&lt;/h3&gt;

&lt;p&gt;We are not going to use some particular value, so we expect to access the type &lt;code&gt;UiCore.ButtonType&lt;/code&gt; without having to import anything. Currently, we don't have &lt;code&gt;UiCore&lt;/code&gt; in the &lt;code&gt;global&lt;/code&gt; scope and thus the code below does not work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// example/application/moduleWithType.ts&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;showNotificationWithButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;buttonText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;buttonType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UiCore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ButtonType&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- TS2503: Cannot find namespace 'UiCore'&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello world!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;buttonText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;buttonType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
TS2503: Cannot find namespace 'UiCore'





&lt;p&gt;Obviously, we are going to add the namespace in the &lt;code&gt;global&lt;/code&gt; scope. Unfortunately, we can't just use the namespace created earlier, we need to define a new one. The trick is to create a new namespace with the same name and with almost the same data included. Good news: instead of importing everything again, we can use our existing namespace to clone the data in form of types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UiCore&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;_UiCore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./namespaces/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UiLib&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./interfaces/ui&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;_UiCore&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;UiCore&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nx"&gt;UiCore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;NotificationButtonConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_UiCore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NotificationButtonConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_UiCore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ButtonType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UiLib&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
we create types from the existing namespace using the type alias syntax





&lt;p&gt;We first rename the &lt;code&gt;UiCore&lt;/code&gt; import as we want to avoid name conflict. Then we re-export &lt;code&gt;UiCore&lt;/code&gt; under the correct name as it was done previously. Finally, we copy the &lt;code&gt;UiCore&lt;/code&gt; namespace items under the global scope. Both namespaces (&lt;code&gt;UiCore&lt;/code&gt; and global &lt;code&gt;UiCore&lt;/code&gt;) export the same data. The only thing I want to draw your attention to is the way how we write export statements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// UiCore under the global scope&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;buttonLists&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ButtonType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// UiCore that can be used as a value&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ButtonType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ButtonType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
different syntax for each case





&lt;p&gt;You can see the global namespace uses &lt;a href="https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-aliases"&gt;type alias&lt;/a&gt; syntax to define objects. For import statements, we want to have values (not types) accessible, so we can't use the same approach there. Instead, we import values and re-export them under the namespace using the composite &lt;code&gt;export import&lt;/code&gt; operator. Thus, we collect all the constants, models, enums, interfaces under some common name, we can name it whatever we want, and it will be a single entry point for all our UI library-related data. As the result, we collected all data in one place, and the developer experience does not change from using the global object to having to import something.&lt;/p&gt;

&lt;p&gt;This part is a tradeoff to get all usage cases working. It adds some copy-paste routine, but then it is a comfortable way to supply developers with type definitions: we can use the global variable exposed by the UI library as we do in JavaScript modules — without having to import anything. Then we can import the package and use constant values. All of them are defined and ready to use. The existing code will remain the same. And yes, we do support the new &lt;code&gt;import type { UiCore } from "ui-types-package"&lt;/code&gt; syntax which was introduced in TypeScript v3.8 to define types. There is no conflict with our implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;You can find thousands of existing type definitions for JavaScript libraries. In this article, I tried to explain some specific edge case, where along with type definitions, the package needs to contain real values. We use this approach for our UI components library to style table cells, specify icons, and more. You can achieve such capabilities by following these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create and set up a new NPM package.&lt;/li&gt;
&lt;li&gt;Describe the whole interface supported by the JavaScript library you want to write type definitions for.&lt;/li&gt;
&lt;li&gt;Declare the global object that is being injected into &lt;code&gt;window&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create a namespace made of objects you have defined already - you will use it for import statements.&lt;/li&gt;
&lt;li&gt;Create a namespace made of types based on the previous namespace. It will be located in the global scope.&lt;/li&gt;
&lt;li&gt;Verify that we assigned the same name for both namespaces.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This small guide makes it possible to cover all potential use cases for any available JS library. In the end, you will get a package, that is easy to use, support, and extend.&lt;/p&gt;

&lt;p&gt;The name &lt;code&gt;UiCore&lt;/code&gt;, the package &lt;code&gt;ui-types-package&lt;/code&gt;, and all objects in the article are placeholders to show the approach. You can use whatever names you want for your libraries and follow the idea described here.&lt;/p&gt;

&lt;p&gt;Complete code example is located &lt;a href="https://github.com/n0th1ng-else/typescript-types-complex-example"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>What I Learned From My Contribution to Angular</title>
      <dc:creator>Sergey Nikitin</dc:creator>
      <pubDate>Fri, 02 Apr 2021 16:28:37 +0000</pubDate>
      <link>https://dev.to/n0th1ng_else/what-i-learned-from-my-contribution-to-angular-h9j</link>
      <guid>https://dev.to/n0th1ng_else/what-i-learned-from-my-contribution-to-angular-h9j</guid>
      <description>&lt;p&gt;ANGULAR 9 has come, and you might notice my name in the commit history. This was the first time I contributed to such a big and widely-used project. The journey appeared to be easy enough and very exciting! Was it so? Let me tell you the story.&lt;/p&gt;

&lt;p&gt;In our company, we went through a bunch of technologies and approaches on how to develop frontend code. Initially, our service was written using the Grails framework — Java-based MVC server-side rendering engine. JQuery used to add some interactions with customers on the page which was quite common those days, but you definitely don’t want to see this in 2020.&lt;/p&gt;

&lt;p&gt;A few years later, AngularJS showed up and it was like fresh air in frontend development. Services, components, template rendering engine, etc. We were happy to implement AngularJS on our platform and wrote over 700k lines of code.&lt;/p&gt;

&lt;p&gt;Time flew and one day Angular (which was expected to be AngularJS v2) was released. The problem was that these two things are not compatible with each other, so our codebase became legacy at one moment. I pushed hard to update the version of the AngularJS and gave up on v1.5.11. What was the solution? We decided to keep existing applications as it is and to select a new way to write frontend inside the company. The thing is that our platform consists of independent applications each of them loads unrelatedly to others. Thus, every application can be written using any library or framework the team decided to use.&lt;/p&gt;

&lt;p&gt;First of all, we switched to build js code with Webpack and removed Browserify for goods. This brought us a lot of opportunities like how we split bundles, what JS features are supported, and so forth. Then the time has come and we added Typescript. After all, we implemented React on the platform. Currently, engineers develop new applications using React. But fair to say that our vision remains the same: each team decided what to use on their own. Some teams still use AngularJS because it is too difficult to re-implement existing logic. Others still fix Grails applications (yes, we still have a few of them in production at the moment!).&lt;/p&gt;

&lt;p&gt;The idea to tune up the infrastructure for Angular was flying in the air, but it was quite tricky until we started to use Webpack. With Webpack it seemed to be an easy deal: load CSS modules with raw-loader, fix Jest HTML templates loader and we good. Good enough? I thought so until we started to write an application based on the Angular framework…&lt;/p&gt;

&lt;h1&gt;
  
  
  Something Went Wrong
&lt;/h1&gt;

&lt;p&gt;The problem appeared from a place we did not expect. Let’s make some introduction: we use &lt;a href="http://ui.tradeshift.com/v12/#intro/" rel="noopener noreferrer"&gt;UI component package&lt;/a&gt; which renders a beautiful and strict UI experience for our clients. This library is framework-agnostic and acts quite similar to Material UI components for Angular i.e. developer forms a specific HTML layout to bring particular component into action, populate and style components by applying pre-defined element classes and tags. Also, one can access any element in the JS code and play with it dynamically.&lt;/p&gt;

&lt;p&gt;So it was my colleague Irina, who found an interesting issue. She was the first person to try Angular on our platform. Historically, the UI components we use rely on the HTML element attributes. These attributes have the form of &lt;strong&gt;data-ts&lt;/strong&gt; or &lt;strong&gt;data-ts.something&lt;/strong&gt;. For instance, if we want to implement &lt;a href="http://ui.tradeshift.com/v12/#components/modals/" rel="noopener noreferrer"&gt;a modal window&lt;/a&gt;, we should add the title by setting &lt;strong&gt;data-ts.title&lt;/strong&gt; attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dialog&lt;/span&gt; &lt;span class="na"&gt;data-ts=&lt;/span&gt;&lt;span class="s"&gt;"Modal"&lt;/span&gt; &lt;span class="na"&gt;data-ts.title=&lt;/span&gt;&lt;span class="s"&gt;"some-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;data-ts=&lt;/span&gt;&lt;span class="s"&gt;"Panel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Modal content.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dialog&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty straightforward, right? But what if we want to apply the title dynamically? Let’s say we want the title to contain a user name or something like this. What should we do? Yes, Angular provides a standard way to interpolate the attribute value from the controller property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dialog&lt;/span&gt; &lt;span class="na"&gt;data-ts=&lt;/span&gt;&lt;span class="s"&gt;"Modal"&lt;/span&gt; &lt;span class="na"&gt;[attr.data-ts.title]=&lt;/span&gt;&lt;span class="s"&gt;"modalTitle"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;data-ts=&lt;/span&gt;&lt;span class="s"&gt;"Panel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Modal content.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dialog&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we go! But wait… what?! This does not look good in the browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9bqtvun5mo8bau9az2vr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9bqtvun5mo8bau9az2vr.png" alt="dialog element is missing data-ts.title attribute"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You may notice here that the Angular compiler went through the HTML template and parsed attributes in the wrong way. For &lt;strong&gt;attr.data-ts.title&lt;/strong&gt; symbol it generates &lt;strong&gt;data-ts&lt;/strong&gt; attribute instead of &lt;strong&gt;data-ts.title&lt;/strong&gt;. This completely breaks the layout and the modal window does not work. UI components even don’t know that I defined a modal window because the attribute was overridden with the result of interpolation. It sounds like a real blocker that prevents us from using Angular.&lt;/p&gt;

&lt;h1&gt;
  
  
  Trying to Find a Solution
&lt;/h1&gt;

&lt;p&gt;I tried to google the solution but had no luck. I felt like it is meant to be a very specific case nobody noticed really. Fair enough. On the other hand, if HTML standard supports this type of element attributes and browsers render them correctly, so should Angular compiler do. Taking this into account, I decided to ask Angular team directly. I went to Angular GitHub &lt;a href="https://github.com/angular/angular" rel="noopener noreferrer"&gt;repository&lt;/a&gt; and opened an &lt;a href="https://github.com/angular/angular/issues/31334" rel="noopener noreferrer"&gt;issue&lt;/a&gt;. The beginning was promising, they marked my issue with tags that highlight that the bug exists in the Angular compiler and the issue has low priority. What happened next? I started waiting…&lt;/p&gt;

&lt;p&gt;…After 2 months nothing really happened. I figured out that since my problem is not something major, then there are low chances to see it fixed any time soon. The team is busy making the Ivy engine into a stable version. Nonetheless, they confirmed the bug exists, so they would not be against me fixing the issue by myself. Okay then, I am doing a fork of the Angular repository.&lt;/p&gt;

&lt;h1&gt;
  
  
  I am Going to Fix the Bug
&lt;/h1&gt;

&lt;p&gt;First of all, I drew my attention to the &lt;a href="https://github.com/angular/angular/blob/master/CONTRIBUTING.md" rel="noopener noreferrer"&gt;CONTRIBUTING.md&lt;/a&gt; file and read it carefully (consider making a project fork to be my zero step). This file describes all the rules I should follow so Angular team will proceed with my Pull Request. It explains the responsibility of the parties, code ownership, agreement on the commit message format, test coverage requirements and many other questions.&lt;/p&gt;

&lt;p&gt;Next, you need to sign the Contributor License Agreement with Google company which confirms you are okay with all contributing rules and restrictions. The CLA link is located at the end of the CONTRIBUTING.md file so read the whole text until the end. Finally, paperwork comes to an end, let’s dive into the project itself.&lt;/p&gt;

&lt;p&gt;Angular is a typical yarn project, just big enough. You can simply run &lt;code&gt;yarn install&lt;/code&gt; and it will setup all the environment. Well, okay, simply run &lt;code&gt;yarn install&lt;/code&gt; and simply wait for 5–7 more minutes — I told you this is a big one!&lt;/p&gt;

&lt;p&gt;I stopped for a second at this point, because I was looking forward to an exciting journey — to go through the source code. It was scary in the beginning to figure out what is going on in such a huge project, tons of modules, different pieces of code interact with each other. But after spending some time I came to the conclusion that big projects mean to be a big advantage.&lt;/p&gt;

&lt;p&gt;When your project becomes huge, you focus on absolutely different things that might sound meaningless when you have just a few files. Different things, different approaches show up to be the priority. In the Angular, I saw that the project structure is a super important thing. Every module has a meaningful name, every variable document itself, every method shows what it does exactly. It is quite easy to navigate through the framework parts and the understanding of what this or that module is for comes to your mind instantly.&lt;/p&gt;

&lt;p&gt;I already knew that my issue lays somewhere in the Angular compiler and it was quite easy to find the &lt;a href="https://github.com/angular/angular/blob/a20979d5f613a5a2c70f26568a0e1dbf4458bf53/packages/compiler/src/template_parser/binding_parser.ts#L281" rel="noopener noreferrer"&gt;exact line&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2awgo1i3rksal73h3rz7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2awgo1i3rksal73h3rz7.png" alt="Render only the first part of an attribute name? It does not sound correct to me…&amp;lt;br&amp;gt;
"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, what is this? We receive an HTML element attribute (stored in the &lt;strong&gt;boundProp&lt;/strong&gt;). Then we split the attribute name by some delimiter to figure out if it contains “&lt;strong&gt;attr&lt;/strong&gt;” prefix. If the first part is the prefix, we consider the attribute name to be equal to the second part. Obviously, this is not correct, we should concatenate all the parts except the prefix instead. Let’s &lt;a href="https://github.com/angular/angular/commit/c0f69f324548ed06ecdbd0b4d307f5585f620fe8#diff-b7c791684b7cf2141d91a16796befebe" rel="noopener noreferrer"&gt;fix it&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl663x3k9a7msp96vtu1k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl663x3k9a7msp96vtu1k.png" alt="Now looks much better! All attribute parts are in place."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perfect! Now we need to make sure other changes would never break our functionality. I am going to write a test. It is quite easy to do in such huge projects like Angular. There is a requirement to cover any code change by unit tests so you definitely will find a spec file along with each module in the repository.&lt;/p&gt;

&lt;p&gt;So I opened that spec file and saw a bunch of tests that cover the module I have changed. I went through the 2000 lines of test cases and found the test which checks the name of the attribute after compiler parsed some HTML element. Made a copy and changed attribute name, so now it contains a dot delimiter and fixed the output expectation. That’s it. Now my changes are covered! I thought it would be tough, but turn out it was super easy:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjea6vhsgw52vmkfau4mb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjea6vhsgw52vmkfau4mb.png" alt="All I need to cover my changes is one line test"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, I fixed the code. Writing a commit message keeping in mind the format team asked to follow and… going to the kitchen to make some tea as the pre-commit hook launches all unit tests in the project. Can you imagine having 37000+ tests in the project? The first launch will take a while, but then test results will be cached and next runs will take much less time. I made a commit, opened a &lt;a href="https://github.com/angular/angular/pull/32256" rel="noopener noreferrer"&gt;Pull Request&lt;/a&gt;, started waiting…&lt;/p&gt;

&lt;h1&gt;
  
  
  Resolution
&lt;/h1&gt;

&lt;p&gt;…After 2 months I decided waiting is enough for me. The code was the smaller problem, the bigger one was how to proceed with my changes? It was clear to me that I need to find a person who is somehow related to the Angular team and discuss what options I have right now. I applied the social engineering approach and went through the latest contributors to the Angular compiler module. &lt;a href="https://github.com/AndrewKushnir" rel="noopener noreferrer"&gt;Andrew Kushnir&lt;/a&gt; was the first on my mind. I found his Facebook account and wrote him a message. Unfortunately, a week after I did not get a follow back and decided not to be annoying for the person who doesn’t know me. I decided to have another try and found &lt;a href="https://github.com/petebacondarwin" rel="noopener noreferrer"&gt;Pete Bacon Darwin&lt;/a&gt;. I noticed that his profile has an email address so it might be useful even in 2019. And it actually did work!&lt;/p&gt;

&lt;p&gt;I wrote a long email describing all the little things we struggle with. Described our UI library, attached links on the issue and pull-request. I told about myself and my company. And most important I was really thankful as expecting Pete to spend his own time on my problem.&lt;/p&gt;

&lt;p&gt;The next day I found a message from Pete. He told me that he is okay to contact via email about my pull request and that he never saw people define attributes with dot notation. He also gave me a piece of advice on how to write an accurate commit message. And then, he approved my code changes. After this, all happened with the speed of light… while I was sleeping. (guess why? 12 hours difference between the US and Novosibirsk — the place where I live)&lt;/p&gt;

&lt;p&gt;While I was sleeping, the Angular team moved my pull request into a release state. They ran another set of tests (I guess they were integration tests this time) and figured out that in one test there was an SVG-block with some interesting attribute &lt;strong&gt;[attr.height.px]=”16"&lt;/strong&gt;. It used to be rendered as &lt;strong&gt;height=”16"&lt;/strong&gt;, but with my changes, it became &lt;strong&gt;height.px=”16"&lt;/strong&gt;. After a small discussion, they decided to fix that SVG block and to merge my changes into Angular. This meant that my fix will show up in the upcoming major version of the framework!&lt;/p&gt;

&lt;p&gt;My commit was merged, my pull request was closed. I just got up in the morning and saw all these comments, was scared, upset, and glad at the same time.&lt;/p&gt;

&lt;p&gt;In the evening I got an email from Pete. He confirmed that my fix will be released with Angular v9 and thanked me for contributing to the project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F06ipz82shxzw6qnxnlm1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F06ipz82shxzw6qnxnlm1.png" alt="That exact email"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From my side, I paid appreciation for the time he spent on me and for all the help. I said that it was quite an important step for me to make things right and to make a success.&lt;/p&gt;

&lt;p&gt;You may ask me what’s next?&lt;/p&gt;

&lt;p&gt;Next few months I was waiting for the release. I noticed that my changes came with Angular 9.0.0-rc.2. And now, literally, a month ago they have released the stable version of the &lt;a href="https://blog.angular.io/version-9-of-angular-now-available-project-ivy-has-arrived-23c97b63cfa3" rel="noopener noreferrer"&gt;Angular 9&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  The outcome
&lt;/h1&gt;

&lt;p&gt;For me, it was a success story. We finally got Angular based applications working on the platform. But the most important thing here is that I did my best to gain the result. So, go all the way and never give up. Try different ways to achieve what you want. Push the stuff you think is right. Look for people who can help you to find a way to deliver.&lt;/p&gt;

&lt;p&gt;If you see a big popular project in front of you, it does not mean you can’t influence it. If you have an idea or you believe something works not as expected — just try. Even if the cost of your changes is a one-line fix. In most cases, the team is open to external contributions and any help from other developers. They might have no time to fix this or that issue. So you are welcome to offer your help.&lt;/p&gt;




&lt;p&gt;I have this article posted in Russian as well, so if you want you can &lt;a href="https://habr.com/ru/post/491788/" rel="noopener noreferrer"&gt;read the translated version here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
