<?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: Pouja</title>
    <description>The latest articles on DEV Community by Pouja (@pouja).</description>
    <link>https://dev.to/pouja</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%2F101724%2F059f7bb1-f4d4-4384-be64-74aa32104d3f.png</url>
      <title>DEV Community: Pouja</title>
      <link>https://dev.to/pouja</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pouja"/>
    <language>en</language>
    <item>
      <title>Everything about ESM and treeshaking</title>
      <dc:creator>Pouja</dc:creator>
      <pubDate>Sat, 12 Jul 2025 20:05:51 +0000</pubDate>
      <link>https://dev.to/pouja/everything-about-esm-and-treeshaking-5f4l</link>
      <guid>https://dev.to/pouja/everything-about-esm-and-treeshaking-5f4l</guid>
      <description>&lt;p&gt;With increasing support of ESM in Typescript &lt;a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html#ecmascript-module-support-in-nodejs" rel="noopener noreferrer"&gt;1&lt;/a&gt; &lt;a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-4.html#support-for-require-calls-in---moduleresolution-bundler-and---module-preserve" rel="noopener noreferrer"&gt;2&lt;/a&gt; &lt;a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-8.html#support-for-require-of-ecmascript-modules-in---module-nodenext" rel="noopener noreferrer"&gt;3&lt;/a&gt; and NodeJS &lt;a href="https://nodejs.org/en/blog/announcements/v20-release-announce#custom-esm-loader-hooks-nearing-stable" rel="noopener noreferrer"&gt;1&lt;/a&gt; &lt;a href="https://nodejs.org/en/blog/announcements/v22-release-announce#support-requireing-synchronous-esm-graphs" rel="noopener noreferrer"&gt;2&lt;/a&gt; &lt;a href="https://nodejs.org/en/blog/release/v23.0.0#requireesm-is-now-enabled-by-default" rel="noopener noreferrer"&gt;3&lt;/a&gt;, it becomes easier and easier to write your frontend or backend in ESM format. Using the ESM module system has better support for treeshaking when using &lt;a href="https://esbuild.github.io/api/#tree-shaking" rel="noopener noreferrer"&gt;&lt;code&gt;esbuild&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://webpack.js.org/guides/tree-shaking/" rel="noopener noreferrer"&gt;&lt;code&gt;webpack&lt;/code&gt;&lt;/a&gt;. Also with complexity rising of your backend and frontend, it is more than ever important to look at your bundle sizes. Even just recently AWS has announced that everyone, not just people using custom runtime environments, has to pay for &lt;a href="https://aws.amazon.com/blogs/compute/aws-lambda-standardizes-billing-for-init-phase/" rel="noopener noreferrer"&gt;the INIT Duration&lt;/a&gt;, making it also cost effective to have your AWS Lambda functions as small as possible.&lt;/p&gt;

&lt;p&gt;I want to bring you through my journey of understanding the difference between CommonJS and ESM and why it allows for better treeshaking. &lt;br&gt;
I will talk briefly about which typescript config rules and eslint rule might help to reduce the bundle. I will explain what does get removed with treeshaking (functions, methods, classes etc.) and what does not get removed and why that is.&lt;/p&gt;
&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Module System&lt;/li&gt;
&lt;li&gt;Measuring&lt;/li&gt;
&lt;li&gt;CJS to ESM&lt;/li&gt;
&lt;li&gt;CJS vs ESM&lt;/li&gt;
&lt;li&gt;ESM Dynamic Import&lt;/li&gt;
&lt;li&gt;Proper Imports&lt;/li&gt;
&lt;li&gt;Barrel Files&lt;/li&gt;
&lt;li&gt;Unused Code&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  TLDR;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Set &lt;code&gt;module&lt;/code&gt; and &lt;code&gt;moduleResolution&lt;/code&gt; in your TS Config to &lt;code&gt;nodenext&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;.mts&lt;/code&gt; as file extensions or set &lt;code&gt;type&lt;/code&gt; to &lt;code&gt;module&lt;/code&gt; in your nearest &lt;code&gt;package.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Avoid barrel files or set &lt;code&gt;sideEffects&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt; in your nearest &lt;code&gt;package.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;@typescript-eslint/no-unused-vars&lt;/code&gt; to &lt;code&gt;error&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;@typescript-eslint/consistent-type-imports&lt;/code&gt; to &lt;code&gt;error&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Update your &lt;code&gt;webpack&lt;/code&gt;/&lt;code&gt;esbuild&lt;/code&gt; config to also read &lt;code&gt;module&lt;/code&gt; entry of your npm packages &lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Module System
&lt;/h2&gt;

&lt;p&gt;To be able to use functions or classes written in one file in another file requires a way of telling how the files should be linked. In the days of gulp/grunt and jquery there was not much choice, we all wrote everything on the &lt;code&gt;window&lt;/code&gt; object. Everything was accessible by all objects and functions. We had to use closures to isolate &lt;code&gt;var&lt;/code&gt; variables from another and prevent naming collisions. It was basically just one large JavaScript file. Quite a nightmare.&lt;/p&gt;
&lt;h3&gt;
  
  
  CommonJS
&lt;/h3&gt;

&lt;p&gt;Luckily, people quickly started writing systems to separate them, not only at when you are writing your code but also at runtime. One of the first one and one that still lives to today is CommonJS. Everyone writing in plain NodeJS is probably already familiar with this syntax:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getHelloWorld&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;getHelloWorld&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;



&lt;p&gt;The fact that everything was loaded synchronously, which was not really an issue at that time when writing for servers, it was not really feasible for front-ends. Therefore &lt;a href="https://requirejs.org/" rel="noopener noreferrer"&gt;RequireJS&lt;/a&gt; was brought to life. If you ever wondered how it looks, there is &lt;a href="https://github.com/volojs/create-template" rel="noopener noreferrer"&gt;an example repository&lt;/a&gt; still living. If you are more interested in the history, look up: AMD, &lt;a href="https://github.com/umdjs/umd" rel="noopener noreferrer"&gt;UMD&lt;/a&gt;, RequireJS.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Small note: I also believe people just wanted to write their own module systems, who can blame their enthusiasm? It was a great module system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The problem with CommonJS is that the &lt;code&gt;require&lt;/code&gt; function is a function.&lt;br&gt;
That means the following is valid:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&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;fs1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;f&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;s&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;fs2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;f&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;s`&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;fs3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;f&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;s&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&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;modules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getFS&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;reallyGetIt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;reallyGetIt&lt;/span&gt;&lt;span class="p"&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;fs6&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getFS&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;allTheSame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fs1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fs2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fs3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fs4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fs6&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;every&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;fsModule&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;fsModule&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allTheSame&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Please do not do this&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is also why your javascript file is executed when referenced, because NodeJS has to know what it has to import. That is the largest reason why &lt;code&gt;esbuild&lt;/code&gt; or &lt;code&gt;webpack&lt;/code&gt; can not treeshake properly. Because they can not do any static analysis to create a tree of how your program is linked together. The same principle applies when using &lt;code&gt;module.exports&lt;/code&gt;. Where ever you can use an object, you can export your function or variable.&lt;/p&gt;

&lt;p&gt;Another reason CommonJS makes it hard but not impossible, is the freedom of how you reference other files or packages.&lt;br&gt;
Given the following code:&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="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../libs/manager&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;



&lt;p&gt;This could either be a &lt;code&gt;../libs/manager.js&lt;/code&gt; file or &lt;code&gt;../libs/manager/index.js&lt;/code&gt; or &lt;code&gt;../libs/manager/package.json&lt;/code&gt;. This freedom was quite useful in the early days of NPM, but it adds too much ambiguity for the developers. There are more "quirks" to CommonJS, if you are interested in more details, a good start would be &lt;a href="https://nodejs.org/api/modules.html" rel="noopener noreferrer"&gt;the NodeJS documentation&lt;/a&gt; itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  ESM
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;require&lt;/code&gt; is not defined within &lt;a href="https://tc39.es/ecma262/" rel="noopener noreferrer"&gt;EcmaScript&lt;/a&gt; and it is not natively implemented by any browser. It is something of NodeJS itself built upon v8. The same applies for the AMD, UMD and System module syntax. EcmaScript was behind the ecosystem, but that was a great plus because it could look at all the existing module system and draft a better one.&lt;/p&gt;

&lt;p&gt;Entering ESM. It has similar syntax to what Typescript does:&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;readRootPackage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./package.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no way to tell if this is ESM or Typescript without looking at the file extension. This also adds to confusion for developers that they are not sure if this will get compiled to a &lt;code&gt;require&lt;/code&gt; or the native EcmaScript &lt;code&gt;import&lt;/code&gt; operator. The only way you know for sure what the output would be, is by compiling it.&lt;/p&gt;

&lt;p&gt;What makes ESM so great that it allows for treeshaking? The &lt;code&gt;import&lt;/code&gt; is an operator. Meaning that other rules apply compared to &lt;code&gt;require&lt;/code&gt; function of NodeJS. The most important rule is that it only allows static strings:&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&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="nx"&gt;fs1&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;f&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// error&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs2&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;f&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// error&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs3&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;f&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;s`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// error&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs4&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// error&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs5&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getFS&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// allowed&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;fs6&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getFS&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;fs6&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows anyone to perform static analysis and build a tree of how the program is linked together. Furthermore, to prevent ambiguity you either reference a package or a file. That is why the file extensions are required. This is also where people get confused when they are writing typescript but they have to use &lt;code&gt;.js&lt;/code&gt; file extensions, luckily this is fixed by using &lt;code&gt;nodenext&lt;/code&gt; as &lt;code&gt;moduleResolution&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you are interested in more details about esm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://exploringjs.com/js/book/ch_modules.html" rel="noopener noreferrer"&gt;Detailed explanation&lt;/a&gt; written by &lt;a href="https://dr-axel.de/" rel="noopener noreferrer"&gt;Dr. Axel Rauschmayer&lt;/a&gt;.
Also check out his &lt;a href="https://2ality.com/" rel="noopener noreferrer"&gt;blog&lt;/a&gt;, always very well written and comprehensive.&lt;/li&gt;
&lt;li&gt;Mozilla &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules" rel="noopener noreferrer"&gt;explanation&lt;/a&gt; on JavaScript imports.&lt;/li&gt;
&lt;li&gt;NodeJS &lt;a href="https://nodejs.org/api/esm.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://v8.dev/features/modules" rel="noopener noreferrer"&gt;V8&lt;/a&gt; on ESM imports impact on the browser.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://262.ecma-international.org/15.0/index.html#sec-ecmascript-language-scripts-and-modules" rel="noopener noreferrer"&gt;Language specification&lt;/a&gt; on ESM by ECMAScript.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Measuring
&lt;/h2&gt;

&lt;p&gt;Before we dive in to how much difference it makes between CJS and ESM and how you can transition. It is useful to know how you can measure it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;webpack&lt;/code&gt; and &lt;code&gt;esbuild&lt;/code&gt; both support emitting a metafile, which contains data which npm packages have been imported, why they are imported and which module system is used to import them. With &lt;code&gt;webpack&lt;/code&gt; you can use the &lt;code&gt;--profile&lt;/code&gt; &lt;a href="https://webpack.js.org/configuration/other-options/#profile" rel="noopener noreferrer"&gt;flag&lt;/a&gt;. With &lt;code&gt;esbuild&lt;/code&gt; you can use the &lt;code&gt;--metafile=bundle.meta.json&lt;/code&gt; &lt;a href="https://esbuild.github.io/api/#build-metadata" rel="noopener noreferrer"&gt;flag&lt;/a&gt; or the property &lt;code&gt;{meta: true}&lt;/code&gt; that will return the json object containing the meta data, which you can write to the file system yourself.&lt;/p&gt;

&lt;p&gt;You can submit the meta file to &lt;a href="https://esbuild.github.io/analyze/" rel="noopener noreferrer"&gt;https://esbuild.github.io/analyze/&lt;/a&gt; and then you will get a visual representation on how everything is bundled.&lt;br&gt;
You can let it mark visually which packages are interpreted as CommonJS and which ones as ESM. This will give you the knowledge if you imported them correctly and if &lt;code&gt;esbuild&lt;/code&gt; was able to find the ESM bundle of the npm package.&lt;/p&gt;
&lt;h2&gt;
  
  
  CJS to ESM
&lt;/h2&gt;

&lt;p&gt;To show you the difference, I have created 2 projects, both have similar code but one is written for &lt;a href="https://github.com/Pouja/configuring-esm/tree/main/ts-cjs" rel="noopener noreferrer"&gt;CommonJS&lt;/a&gt; and the other for &lt;a href="https://github.com/Pouja/configuring-esm/tree/main/ts-esm" rel="noopener noreferrer"&gt;ESM&lt;/a&gt;. You can clone the repository and play with the different ways of importing a file and see what impact it will make. This should also help you transitioning to ESM by looking at the differences. I believe more in learning by doing than in learning by reading.&lt;br&gt;
So I hope this helps for you.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;esbuild&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Both projects contain a &lt;code&gt;esbuild.config.(m)js&lt;/code&gt; file. Depending on your own project, this configuration is in most cases either generated or hidden from you. The most important configurations are:&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;mainFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;module&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;main&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Tells which package.json entries it should look for&lt;/span&gt;
    &lt;span class="nx"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Tells if it should expect window object to be present, or the NodeJS internal packages&lt;/span&gt;
    &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;esm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Tells if the output should be in ESM format or in CommonJS&lt;/span&gt;
    &lt;span class="nx"&gt;metafile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// If true, it will return a meta file object&lt;/span&gt;
    &lt;span class="nx"&gt;minify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Will eliminate any unreachable statement, shorten all names and remove all whitespaces where possible&lt;/span&gt;
    &lt;span class="nx"&gt;tsconfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tsconfig.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// It only reads some properties from the tsconfig, see also TsconfigRaw in main.d.ts of `esbuild`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;esbuild&lt;/code&gt; has its own transpiler, so that is why only some properties are read from the tsconfig. But the exception is when you use a plugin like &lt;a href="https://www.npmjs.com/package/esbuild-decorators" rel="noopener noreferrer"&gt;&lt;code&gt;esbuild-decorator&lt;/code&gt;&lt;/a&gt;, which does use all the settings from your tsconfig. So you can not just configure your tsconfig to use CommonJS and then tell &lt;code&gt;esbuild&lt;/code&gt; to output in ESM, you will most likely get some runtime errors.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;mainFields&lt;/code&gt; property is quite important. Only a few modern libraries now output it properly. By properly I mean using the &lt;code&gt;exports&lt;/code&gt; field in the package.json, to tell NodeJS module resolver which file it should read depending on if you are using ESM or CommonJS. If we take a look at a part of the package.json of &lt;code&gt;class-validator&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"class-validator"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sideEffects"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&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;"./cjs/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;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./esm5/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;"es2015"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./esm2015/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;"typings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./types/index.d.ts"&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;



&lt;p&gt;NodeJS will by default only look at the &lt;code&gt;exports&lt;/code&gt; field and if that is not present, which is the case for class-validator, it will use the &lt;code&gt;main&lt;/code&gt; field.&lt;br&gt;
So even if you do this:&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;// index.mjs&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;validate&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="s1"&gt;class-validator&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;



&lt;p&gt;NodeJS will look at &lt;code&gt;node_modules/class-validator/cjs/index.js&lt;/code&gt; even though your file is in &lt;code&gt;.mjs&lt;/code&gt; format and even if your package.json you have set &lt;code&gt;type&lt;/code&gt; to &lt;code&gt;module&lt;/code&gt;. You can still tell &lt;code&gt;esbuild&lt;/code&gt; to use the ESM output of class-validator by setting the &lt;code&gt;mainFields&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;Use this information!&lt;br&gt;
When inspecting and investigating your imports and you notice it uses the CommonJS variant of the package, even when they have ESM files. Look at their package.json! If they do not emit any ESM file, your only options are: submitting a pull request or copy the code to your code base.&lt;/p&gt;
&lt;h3&gt;
  
  
  package.json
&lt;/h3&gt;

&lt;p&gt;The property &lt;code&gt;type&lt;/code&gt; in the package.json is set to &lt;code&gt;module&lt;/code&gt; for the ESM project and omitted CommonJS project, which defaults to &lt;code&gt;commonjs&lt;/code&gt;. You can actually choose how you will mark your project as ESM or CommonJS. When using plain javascript, you either use the &lt;code&gt;.mjs&lt;/code&gt; extension for your files or you set the &lt;code&gt;type&lt;/code&gt; property in the nearest package.json. You can mix. It is discouraged to use CommonJS files in ESM files, but not the other way around.&lt;/p&gt;

&lt;p&gt;If you are using CommonJS and you need to use a package that only has ESM files, you can use the following "hack":&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;myFunc&lt;/span&gt;&lt;span class="p"&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;dynamicImport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;specifier&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;return import(specifier)&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;TypeDoc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;dynamicImport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;typedoc&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  tsconfig.json
&lt;/h3&gt;

&lt;p&gt;This is the most confusing part. I really had to read through the &lt;a href="https://www.typescriptlang.org/docs/handbook/modules/reference.html" rel="noopener noreferrer"&gt;module page&lt;/a&gt; of typescript a couple of times to understand what they are saying. The most important configurations for transition of CJS to ESM are:&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;"compileOnSave"&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;"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;"moduleResolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nodenext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tells&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;typescript&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;which&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;algorithm/method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;should&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;resolve&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;imports&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NodeNext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tells&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;CommonJS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;AMD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;System&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ESM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;mix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;CommonJS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ESM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;which&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;nodenext&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ESNext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tells&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;how&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;should&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;look&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;like&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;after&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;compiled&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;javascript&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;



&lt;p&gt;&lt;code&gt;nodenext&lt;/code&gt; basically means that typescript will either resolve the imports by using NodeJS CommonJS module resolution technique or using the new ESM module resolution technique. And it depends on the file extensions, using &lt;code&gt;.mts&lt;/code&gt; will tell &lt;code&gt;typescript&lt;/code&gt; to use ESM. If you use &lt;code&gt;.ts&lt;/code&gt; but the package.json has the property &lt;code&gt;type&lt;/code&gt; set to &lt;code&gt;module&lt;/code&gt; it will use ESM. In all other cases it will use CommonJS. Although typescript is quite lenient since 5.8.x on which module system it will use, it is best to be consistent.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Do not mix! You will get unexpected results when using frameworks that can possibly use different methods to determine ESM vs CommonJS!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can also set &lt;code&gt;moduleResolution&lt;/code&gt; to &lt;code&gt;bundler&lt;/code&gt;, but then you have to specify to which specific module system you want your javascript files compiled to. Then it does not matter what file extension you use, it will always output to what ever module system you have picked.&lt;/p&gt;

&lt;p&gt;When using &lt;code&gt;nodenext&lt;/code&gt; configuration value you can still output to both CommonJS format and ESM format, you only need to change the &lt;code&gt;format&lt;/code&gt; property in &lt;code&gt;esbuild&lt;/code&gt; and you have to make sure to not set the &lt;code&gt;type&lt;/code&gt; property in the package.json file.&lt;/p&gt;

&lt;p&gt;If you are not intending on publishing and if all your libraries/framework support ESM, which Jest does do quite well right now, I would recommend to setting the &lt;code&gt;type&lt;/code&gt; property to &lt;code&gt;module&lt;/code&gt; and use the &lt;code&gt;.mts&lt;/code&gt; file extension. You will notice  that you have to use &lt;code&gt;.mjs&lt;/code&gt; when importing a relative file. Do not worry, you are not importing a javascript file, Typescript will automatically search for any &lt;code&gt;.mts&lt;/code&gt; file with the same module name.&lt;br&gt;
It is confusing, people have &lt;a href="https://github.com/microsoft/TypeScript/issues/42151" rel="noopener noreferrer"&gt;complained&lt;/a&gt; about this behavior, but it is what it is.&lt;/p&gt;
&lt;h2&gt;
  
  
  CJS vs ESM
&lt;/h2&gt;

&lt;p&gt;Now you know how you can transition, but how much will the difference be?&lt;/p&gt;

&lt;p&gt;CommonJS:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F4q114qoyfaol8w8vznht.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F4q114qoyfaol8w8vznht.png" alt="Bundle size for CommonJS" width="780" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ESM:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fwrm24t6u9xwujrs7qlg6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fwrm24t6u9xwujrs7qlg6.png" alt="Bundle size for ESM" width="780" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is 20x smaller when using the same code.&lt;br&gt;
If you generate the metafiles and upload it to &lt;code&gt;esbuild&lt;/code&gt; page, you can reproduce this result:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;git clone https://github.com/Pouja/configuring-esm.git&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fnm use&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cd ts-cjs &amp;amp;&amp;amp; npm install &amp;amp;&amp;amp; npm run bundle:ts &amp;amp;&amp;amp; cd -&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cd ts-esm &amp;amp;&amp;amp; npm install &amp;amp;&amp;amp; npm run bundle:ts &amp;amp;&amp;amp; cd -&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Open in the browser &lt;code&gt;https://esbuild.github.io/analyze/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Upload the &lt;code&gt;metafile.json&lt;/code&gt; in the &lt;code&gt;dist&lt;/code&gt; folders&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You will notice that the biggest difference comes from &lt;code&gt;libphonenumber-js&lt;/code&gt; not being present, because due to ESM, &lt;code&gt;esbuild&lt;/code&gt; knows it can remove it from the bundle safely.&lt;/p&gt;
&lt;h2&gt;
  
  
  ESM Dynamic Import
&lt;/h2&gt;

&lt;p&gt;For the readers that were paying attention, I went through create details saying that using dynamic module names for &lt;code&gt;require&lt;/code&gt; is one of the reasons treeshaking is not possible. You could be wondering: "ESM supports &lt;code&gt;await import(moduleName)&lt;/code&gt;, so how will that work?"&lt;br&gt;
Great question!&lt;/p&gt;

&lt;p&gt;ESM does allow the following:&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;// on-ssm-event.ts&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProxyApiGatewayEventv2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Manager&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;superUnsafeThis&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;



&lt;p&gt;&lt;code&gt;esbuild&lt;/code&gt; will in this case do nothing special. You are responsible yourself to make sure it resolves to something at runtime. If you do expect them to be present when dynamically importing them then you need to reference them somewhere.&lt;/p&gt;

&lt;p&gt;There are some other interesting results:&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;// on-ssm-event.ts&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProxyApiGatewayEventv2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// esbuild will bundle all files that are reachable from one folder up&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;manager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;superUnsafeThis&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// esbuild will bundle both files&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;manager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isS3Event&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../lib/use-s3.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../lib/use-sqs.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

     &lt;span class="c1"&gt;// esbuild will bundle all files under the lib folder, not just the s3 and sqs one&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;manager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../lib/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isS3Event&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use-s3.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use-sqs.js&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can play around with in &lt;a href="https://github.com/Pouja/configuring-esm/blob/main/barrel-file/src/handlers/on-ssm-event.ts" rel="noopener noreferrer"&gt;the example repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Depending on your bundler you might get different results! Analyze the bundle. For example &lt;a href="https://www.npmjs.com/package/@rollup/plugin-dynamic-import-vars" rel="noopener noreferrer"&gt;@rollup/plugin-dynamic-imports-vars&lt;/a&gt; has different ways it variable imports.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proper Imports
&lt;/h2&gt;

&lt;p&gt;Even when using CommonJS you can reduce your bundle size by looking at the import statements.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;import type { MyClass} from './MyClass';&lt;/code&gt; or &lt;code&gt;import { type MyClass} from './MyClass';&lt;/code&gt; if you only use the import as a type. All typescript &lt;code&gt;type&lt;/code&gt; and &lt;code&gt;interface&lt;/code&gt; do get stripped out by default. But if you use something like &lt;code&gt;typeof MyClass&lt;/code&gt; and you do not import it as a type, then &lt;code&gt;esbuild&lt;/code&gt; will add that class to your bundle. Using &lt;a href="https://typescript-eslint.io/rules/consistent-type-imports/" rel="noopener noreferrer"&gt;@typescript-eslint/consistent-type-imports&lt;/a&gt; allows you to capture most of them. This rule will not work on any file that uses decorators. That is because under the hood the imported value might actually be used when setting the metadata of the decorated property or method.&lt;/p&gt;

&lt;p&gt;Eliminate unused imports by detecting unused variables/functions that might use those. You can detect those by using &lt;a href="https://typescript-eslint.io/rules/no-unused-vars" rel="noopener noreferrer"&gt;@typescript-eslint/no-unused-vars&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Barrel files
&lt;/h2&gt;

&lt;p&gt;Even with using proper imports, with a barrel file you can still end up with that package or file in your bundle. Why?&lt;/p&gt;

&lt;p&gt;First of all, what is a barrel file? When you re-export everything from a folder within a &lt;code&gt;index.ts&lt;/code&gt; that is called a barrel file.&lt;br&gt;
An example of 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;// index.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./use-cloudwatch&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./use-dynamodb&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./use-lambda&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./use-s3&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./use-sqs&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./use-ssm&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./helpers/index&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;



&lt;p&gt;All exported values within each of those files are re-exported by &lt;code&gt;index.ts&lt;/code&gt;. This makes it easier to import any value of any of the files.&lt;br&gt;
No need to have a long listed import statement like: &lt;code&gt;import { mapToZod } from '../lib/helpers/zod-utilities';&lt;/code&gt;, but you can write it as &lt;code&gt;import { mapToZod } from '../lib';&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A lot of blog posts mark barrel files as evil. But it is a bit nuanced. It has impact on two things: runtime and bundle size.&lt;/p&gt;
&lt;h3&gt;
  
  
  Runtime
&lt;/h3&gt;

&lt;p&gt;As explained in the CommonJS section, each file is executed before imported and wrapped by NodeJS. That means when you import a barrel file, NodeJS will go through each file mentioned there, then it will read each require call, load that file etc. When your program is large enough, or you node_modules folder is large enough, this will impact any framework or library you are using. With ESM you still have the same issue, ECMAScript does not verify if an imported module has side effects or not. It will always evaluate all top level expressions in a module, even if you only import a single function.&lt;/p&gt;

&lt;p&gt;Take &lt;code&gt;jest&lt;/code&gt; for example, it will go through all those files before it even runs any test function. I have seen cases where it took 20+ seconds before running the first file, even when most files were not even used. But due to extensive usage of barrel files all those files were transpiled and then executed. By eliminating some of those barrel files, I was able to reduce the start time to 5 seconds.&lt;/p&gt;

&lt;p&gt;If you use your barrel files sparingly or if your program is not large, you are good to go.&lt;/p&gt;
&lt;h3&gt;
  
  
  Bundle Size
&lt;/h3&gt;

&lt;p&gt;What is considered a side effect?&lt;br&gt;
Take the following &lt;a href="https://github.com/Pouja/configuring-esm/blob/main/barrel-file/src/lib/load-dotenv.ts" rel="noopener noreferrer"&gt;file&lt;/a&gt;:&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;// load-dotenv.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dotenv&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;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you import that file: &lt;code&gt;import './load-dotenv';&lt;/code&gt; it will read out the &lt;code&gt;.env&lt;/code&gt; file and update &lt;code&gt;process.env&lt;/code&gt;. This is considered a side effect because you are altering the possible execution of the program by just importing the file. You have more examples of side effects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;import 'reflect-metadata';&lt;/code&gt; when you need to use &lt;code&gt;tsyringe&lt;/code&gt;, it modifies the global &lt;code&gt;Reflect&lt;/code&gt; object. - &lt;code&gt;import 'zone.js';&lt;/code&gt; when you use &lt;code&gt;angular&lt;/code&gt;, overwrites all async calls with &lt;code&gt;zones&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no sure way that &lt;code&gt;esbuild&lt;/code&gt; or &lt;code&gt;webpack&lt;/code&gt; can know that it can safely remove files when re-exporting them without giving any hints.&lt;br&gt;
That is why barrel files can increase your bundle size significantly.&lt;br&gt;
Luckily both of them support &lt;a href="https://esbuild.github.io/api/#ignore-annotations" rel="noopener noreferrer"&gt;1&lt;/a&gt; &lt;a href="https://webpack.js.org/guides/tree-shaking/#clarifying-tree-shaking-and-sideeffects" rel="noopener noreferrer"&gt;2&lt;/a&gt; the &lt;code&gt;sideEffects&lt;/code&gt; property in the &lt;code&gt;package.json&lt;/code&gt;. When set to false it will safely remove any unused re-exported file. But that will also remove &lt;code&gt;load-dotenv.ts&lt;/code&gt;! So be careful when using that property. It does not affect any other npm package you are using as a side effect, only the ones in your project.&lt;/p&gt;

&lt;p&gt;If it is not possible to set the property &lt;code&gt;sideEffects&lt;/code&gt; then you should prevent any re-exporting modules/files. That means no barrel files. That also means no god files. God files is what I refer to values that import everything from everywhere as a single entry point. If you define all your AWS Lambda handlers in one file, that is a god file. If you define all the routes with all the components of your frontend page and do not chunk them, that is a god file.&lt;/p&gt;
&lt;h3&gt;
  
  
  Alternative: Workspaces
&lt;/h3&gt;

&lt;p&gt;A good solution in a large project is to have &lt;code&gt;pnpm&lt;/code&gt; &lt;a href="https://pnpm.io/pnpm-workspace_yaml" rel="noopener noreferrer"&gt;workspaces&lt;/a&gt;, where you have internal libraries. Then per library you can mark if all those files that are exported through the &lt;code&gt;package.json&lt;/code&gt; file have side effects or not. You can find an &lt;a href="https://github.com/Pouja/configuring-esm/blob/main/monorepo-ts-esm-workspaces/packages/base-models/package.json" rel="noopener noreferrer"&gt;example configuration&lt;/a&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@packages/base-models"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"typesVersions"&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;"*"&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;"./enums/*"&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="s2"&gt;"./src/enums/*"&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;"./models/*"&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="s2"&gt;"./src/models/*"&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;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"exports"&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;"./enums/*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./src/enums/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"./models/*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./src/models/*"&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;



&lt;p&gt;The exports field allows you to sub categorize your internal library. The wild card &lt;code&gt;*&lt;/code&gt; allows any subpath (0 or more levels deep) or any file. If you want to limit it to one depth you have to set the subfolder to null: &lt;code&gt;"./enums/not-this-dir/": null&lt;/code&gt;. See &lt;a href="https://nodejs.org/api/packages.html#exports-sugar" rel="noopener noreferrer"&gt;official NodeJS documentation&lt;/a&gt; on the specifications of &lt;code&gt;exports&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;typesVersions&lt;/code&gt; is only necessary if you use CommonJS as module resolution or if you want typescript to refer to &lt;code&gt;.d.ts&lt;/code&gt; files that are not next to your &lt;code&gt;.(m)ts&lt;/code&gt; files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Alternative: TSConfig Paths
&lt;/h3&gt;

&lt;p&gt;Another solution, if you do not use internal libraries through the yarn workspaces or pnpm workspaces, is using &lt;a href="https://www.typescriptlang.org/tsconfig/#paths" rel="noopener noreferrer"&gt;tsconfig path&lt;/a&gt;. Taking the previous &lt;code&gt;package.json&lt;/code&gt; example, you will write it as:&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;"paths"&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;"@packages/base-models/enums/*"&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="s2"&gt;"./src/enums/*"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@packages/base-models/models/*"&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="s2"&gt;"./src/models/*"&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="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;



&lt;h3&gt;
  
  
  Demo
&lt;/h3&gt;

&lt;p&gt;You can play with barrel files in &lt;a href="https://github.com/Pouja/configuring-esm/blob/main/barrel-file" rel="noopener noreferrer"&gt;my example project&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone the repository&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cd barrel-file&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Play with the barrel file, &lt;code&gt;sideEffects&lt;/code&gt; property, side effect file &lt;code&gt;load-dotenv.ts&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;node&lt;/code&gt;esbuild&lt;code&gt;.config.mjs&lt;/code&gt; and see the effects.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Unused Code
&lt;/h2&gt;

&lt;p&gt;I have listed several things that &lt;code&gt;esbuild&lt;/code&gt; might see as dead code depending on the setup of your project.&lt;br&gt;
But what about unused code even when using ESM and no side effects?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Unused methods and properties of classes will not be treeshaking.&lt;/em&gt;&lt;br&gt;
If you use a large npm package within a method, but you actually use the class but not that specific method, you will still end up with that npm package in your bundle.&lt;br&gt;
This is where separation of concerns and cohesion come in.&lt;br&gt;
Make sure to follow that paradigm.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Unused elements in the array.&lt;/em&gt;&lt;br&gt;
In most cases this will not impact your bundle much.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Nested unused functions.&lt;/em&gt;&lt;br&gt;
Only top level objects, primitives and functions are considered for treeshaking.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Unused properties in objects.&lt;/em&gt;&lt;br&gt;
There might be some side effects, so it hard to determine if you can remove that code.&lt;/p&gt;

&lt;p&gt;_Unreachable statements when not using minification in &lt;code&gt;webpack&lt;/code&gt; or in &lt;code&gt;esbuild&lt;/code&gt;. _&lt;br&gt;
If you have functions that are dependent on some build time environment variable, for example &lt;code&gt;if (process.env.NODE_ENV === 'production')&lt;/code&gt;.&lt;br&gt;
Or another example of this, is having a class that has different methods for different environment (browser or os).&lt;br&gt;
Then make sure to enable minification.&lt;br&gt;
Or you can use &lt;a href="https://esbuild.github.io/api/#drop-labels" rel="noopener noreferrer"&gt;labels&lt;/a&gt; to achieve similar behavior.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: There might be bundlers out there that can do better in certain scenarios. That is why measuring is key!&lt;/p&gt;

&lt;p&gt;I did &lt;a href="https://github.com/Pouja/configuring-esm/blob/main/barrel-file/rollup.config.mjs" rel="noopener noreferrer"&gt;try&lt;/a&gt; &lt;a href="https://rollupjs.org" rel="noopener noreferrer"&gt;rollup&lt;/a&gt; with &lt;a href="https://github.com/terser/terser" rel="noopener noreferrer"&gt;terser&lt;/a&gt; but I saw similar results&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I have listed some things to look out for.&lt;br&gt;
This list might not be exhaustive, but the most important thing is: analyze!&lt;br&gt;
Make sure that the bundler you use has a way to convey which files and which npm packages were included.&lt;br&gt;
This blog post should give you enough information to understand then why some files/code/packages were included.&lt;br&gt;
From there you can start eliminating and changing the structure of your code.&lt;/p&gt;

&lt;p&gt;Happy hunting and happy coding!&lt;br&gt;
Thank you for reading.&lt;/p&gt;

</description>
      <category>esm</category>
      <category>treeshaking</category>
      <category>cjs</category>
      <category>esbuild</category>
    </item>
    <item>
      <title>Making lodash function get type safe</title>
      <dc:creator>Pouja</dc:creator>
      <pubDate>Mon, 05 Apr 2021 15:34:37 +0000</pubDate>
      <link>https://dev.to/pouja/making-lodash-function-get-type-safe-313o</link>
      <guid>https://dev.to/pouja/making-lodash-function-get-type-safe-313o</guid>
      <description>&lt;p&gt;With the introduction of Typescript 4.1 we can use template literal types for writing our generics. This opens up a whole lot of new applications and better typing, also means they are more complex to read.&lt;/p&gt;

&lt;p&gt;In this post I will be going over why and how I implemented the following result:&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="nx"&gt;expectType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;RegExp&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;deeplvl1[1].deeplvl2.deeplvl3[88].deeplvl4.value&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;

&lt;h2&gt;
  
  
  Table of Content
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Why I needed&lt;/li&gt;
&lt;li&gt;The End Result&lt;/li&gt;
&lt;li&gt;How It Works&lt;/li&gt;
&lt;li&gt;Example&lt;/li&gt;
&lt;li&gt;Future Work&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Why &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Context
&lt;/h3&gt;

&lt;p&gt;In my current project we are using &lt;code&gt;react-final-form&lt;/code&gt; to create our forms. In one of our pages we have a huge object (or as we can call it a god object) that is allowed to be edited in several visible sections. This is bad design by nature, but unfortunately this technical debt is too large to currently solve. We wrote several hooks to make our live easier. One of them is:&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useFormValue&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&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;T&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;fieldInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fieldInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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;


&lt;p&gt;Which can be used as&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;DeepNestComponent&lt;/span&gt;&lt;span class="p"&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;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useFormValue&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customers[3].contract.price&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This allows us to create deep nested components that can subscribe to form changes of a specific form input and display the value.&lt;/p&gt;
&lt;h3&gt;
  
  
  The problem
&lt;/h3&gt;

&lt;p&gt;What if the structure of the large object that is being edited changes? We then have to go over all the components and adjust the path. If you have full unit test coverage, a (snapshot) test will probably fail. But I rather have my compiler give me an error than hoping that my unit coverage is sufficient.&lt;/p&gt;
&lt;h2&gt;
  
  
  The End Result &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I ended up writing the following generic&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ResolveType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&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;OrElse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;LeftSide&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;RightSide&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;LeftSide&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ResolveType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;LeftSide&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;RightSide&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; 
    &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;LeftSide&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;[&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="s2"&gt;].&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;RightSide&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;LeftSide&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;LeftSide&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;U&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ResolveType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;U&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;RightSide&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;LeftSide&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;[&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="s2"&gt;]`&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;LeftSide&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;LeftSide&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;U&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;U&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Which is uploaded to &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Pouja" rel="noopener noreferrer"&gt;
        Pouja
      &lt;/a&gt; / &lt;a href="https://github.com/Pouja/typescript-deep-path-safe" rel="noopener noreferrer"&gt;
        typescript-deep-path-safe
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Collections of generics to make deep nested string paths type safe
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;And it can be used as:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ObjecType&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&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;OrElse&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;ObjecType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orElse&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ResolveType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ObjecType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;orElse&lt;/span&gt;&lt;span class="p"&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;nested&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my.deep[3].nested&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orElse&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;

&lt;h2&gt;
  
  
  How it works &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;My starting point was the implementation of template literal types:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/microsoft/TypeScript/pull/40336" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Template literal types and mapped type 'as' clauses
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#40336&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/ahejlsberg" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F4226954%3Fv%3D4" alt="ahejlsberg avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/ahejlsberg" rel="noopener noreferrer"&gt;ahejlsberg&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/microsoft/TypeScript/pull/40336" rel="noopener noreferrer"&gt;&lt;time&gt;Aug 31, 2020&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;This PR implements two new features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Template literal types&lt;/em&gt;, which are a form of string literals with embedded generic placeholders that can be substituted with actual string literals through type instantiation, and&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Mapped type &lt;code&gt;as&lt;/code&gt; clauses&lt;/em&gt;, which provide the ability to transform property names in mapped types.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Template literal types&lt;/h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;Template literal types are the type space equivalent of template literal expressions. Similar to template literal expressions, template literal types are enclosed in backtick delimiters and can contain placeholders of the form &lt;code&gt;${T}&lt;/code&gt;, where &lt;code&gt;T&lt;/code&gt; is a type that is assignable to &lt;code&gt;string&lt;/code&gt;, &lt;code&gt;number&lt;/code&gt;, &lt;code&gt;boolean&lt;/code&gt;, or &lt;code&gt;bigint&lt;/code&gt;. Template literal types provide the ability to concatenate literal strings, convert literals of non-string primitive types to their string representation, and change the capitalization or casing of string literals. Furthermore, through type inference, template literal types provide a simple form of string pattern matching and decomposition.&lt;/p&gt;
&lt;p&gt;Template literal types are resolved as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Union types in placeholders are distributed over the template literal type. For example &lt;code&gt;`[${A|B|C}]`&lt;/code&gt; resolves to &lt;code&gt;`[${A}]` | `[${B}]` | `[${C}]`&lt;/code&gt;. Union types in multiple placeholders resolve to the cross product. For example &lt;code&gt;`[${A|B},${C|D}]`&lt;/code&gt; resolves to &lt;code&gt;`[${A},${C}]` | `[${A},${D}]` | `[${B},${C}]` | `[${B},${D}]`&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;String, number, boolean, and bigint literal types in placeholders cause the placeholder to be replaced with the string representation of the literal type. For example &lt;code&gt;`[${'abc'}]`&lt;/code&gt; resolves to &lt;code&gt;`[abc]`&lt;/code&gt; and &lt;code&gt;`[${42}]`&lt;/code&gt; resolves to &lt;code&gt;`[42]`&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Any one of the types &lt;code&gt;any&lt;/code&gt;, &lt;code&gt;string&lt;/code&gt;, &lt;code&gt;number&lt;/code&gt;, &lt;code&gt;boolean&lt;/code&gt;, or &lt;code&gt;bigint&lt;/code&gt; in a placeholder causes the template literal to resolve to type &lt;code&gt;string&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The type &lt;code&gt;never&lt;/code&gt; type in a placeholder causes the template literal to resolve to &lt;code&gt;never&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some examples:&lt;/p&gt;
&lt;div class="highlight highlight-source-ts js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;EventName&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; `${&lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;Changed`&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;Concat&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;S1&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-smi"&gt;S2&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; `${&lt;span class="pl-smi"&gt;S1&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;${&lt;span class="pl-smi"&gt;S2&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;`&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;ToString&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;string&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-smi"&gt;number&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-smi"&gt;boolean&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-smi"&gt;bigint&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; `${&lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;`&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T0&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;EventName&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s"&gt;'foo'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// 'fooChanged'&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T1&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;EventName&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s"&gt;'foo'&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-s"&gt;'bar'&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-s"&gt;'baz'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// 'fooChanged' | 'barChanged' | 'bazChanged'&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T2&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;Concat&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s"&gt;'Hello'&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s"&gt;'World'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// 'HelloWorld'&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T3&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; `${&lt;span class="pl-s"&gt;'top'&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-s"&gt;'bottom'&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;-${&lt;span class="pl-s"&gt;'left'&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-s"&gt;'right'&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;`&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T4&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;ToString&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s"&gt;'abc'&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-c1"&gt;42&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-c1"&gt;true&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-c1"&gt;-&lt;/span&gt;&lt;span class="pl-c1"&gt;1234n&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// 'abc' | '42' | 'true' | '-1234'&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Beware that the cross product distribution of union types can quickly escalate into very large and costly types. Also note that union types are limited to less than 100,000 constituents, and the following will cause an error:&lt;/p&gt;
&lt;div class="highlight highlight-source-ts js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;Digit&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-c1"&gt;0&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-c1"&gt;1&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-c1"&gt;2&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-c1"&gt;3&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-c1"&gt;4&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-c1"&gt;5&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-c1"&gt;6&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-c1"&gt;7&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-c1"&gt;8&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-c1"&gt;9&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;Zip&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; `${&lt;span class="pl-smi"&gt;Digit&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;${&lt;span class="pl-smi"&gt;Digit&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;${&lt;span class="pl-smi"&gt;Digit&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;${&lt;span class="pl-smi"&gt;Digit&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;${&lt;span class="pl-smi"&gt;Digit&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;`&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// Error&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;del&gt;A template literal placeholder may optionally specify an &lt;code&gt;uppercase&lt;/code&gt;, &lt;code&gt;lowercase&lt;/code&gt;, &lt;code&gt;capitalize&lt;/code&gt;, or &lt;code&gt;uncapitalize&lt;/code&gt; modifier before the type. This modifier changes the casing of the entire replacement string or the first character of the replacement string. For example:&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;EDIT: Based on feedback, the casing modifiers have been replaced by intrinsic string types in #40580.&lt;/p&gt;
&lt;div class="highlight highlight-source-ts js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;GetterName&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; `get${&lt;span class="pl-smi"&gt;Capitalize&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;`&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;Cases&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; `${&lt;span class="pl-smi"&gt;Uppercase&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt; ${&lt;span class="pl-smi"&gt;Lowercase&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt; ${&lt;span class="pl-smi"&gt;Capitalize&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt; ${&lt;span class="pl-smi"&gt;Uncapitalize&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;`&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T10&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;GetterName&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s"&gt;'foo'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// 'getFoo'&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T11&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;Cases&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s"&gt;'bar'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// 'BAR bar Bar bar'&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T12&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;Cases&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s"&gt;'BAR'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// 'BAR bar BAR bAR'&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Template literal types are all assignable to and subtypes of &lt;code&gt;string&lt;/code&gt;. Furthermore, a template literal type &lt;code&gt;`${T}`&lt;/code&gt; is assignable to and a subtype of a template literal type &lt;code&gt;`${C}`&lt;/code&gt;, where &lt;code&gt;C&lt;/code&gt; is a string literal type constraint of &lt;code&gt;T&lt;/code&gt;. For example:&lt;/p&gt;
&lt;div class="highlight highlight-source-ts js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;test&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-s"&gt;'foo'&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-s"&gt;'bar'&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;name&lt;/span&gt;: `get${&lt;span class="pl-smi"&gt;Capitalize&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;`&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-k"&gt;let&lt;/span&gt; &lt;span class="pl-s1"&gt;s1&lt;/span&gt;: &lt;span class="pl-smi"&gt;string&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;name&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
    &lt;span class="pl-k"&gt;let&lt;/span&gt; &lt;span class="pl-s1"&gt;s2&lt;/span&gt;: &lt;span class="pl-s"&gt;'getFoo'&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-s"&gt;'getBar'&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s1"&gt;name&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Type inference supports inferring from a string literal type to a template literal type. For inference to succeed the starting and ending literal character spans (if any) of the target must exactly match the starting and ending spans of the source. Inference proceeds by matching each placeholder to a substring in the source from left to right: A placeholder followed by a literal character span is matched by inferring zero or more characters from the source until the first occurrence of that literal character span in the source. A placeholder immediately followed by another placeholder is matched by inferring a single character from the source.&lt;/p&gt;
&lt;p&gt;Some examples:&lt;/p&gt;
&lt;div class="highlight highlight-source-ts js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;MatchPair&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;S&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;S&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; `[${infer &lt;span class="pl-smi"&gt;A&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;,${infer &lt;span class="pl-smi"&gt;B&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;]` ? &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-smi"&gt;A&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-smi"&gt;B&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt; : &lt;span class="pl-smi"&gt;unknown&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T20&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;MatchPair&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s"&gt;'[1,2]'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// ['1', '2']&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T21&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;MatchPair&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s"&gt;'[foo,bar]'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// ['foo', 'bar']&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T22&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;MatchPair&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s"&gt;' [1,2]'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// unknown&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T23&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;MatchPair&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s"&gt;'[123]'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// unknown&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T24&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;MatchPair&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s"&gt;'[1,2,3,4]'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// ['1', '2,3,4']&lt;/span&gt;

&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;FirstTwoAndRest&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;S&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;S&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; `${infer &lt;span class="pl-smi"&gt;A&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;${infer &lt;span class="pl-smi"&gt;B&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;${infer &lt;span class="pl-smi"&gt;R&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;` ? &lt;span class="pl-kos"&gt;[&lt;/span&gt;`${&lt;span class="pl-smi"&gt;A&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;${&lt;span class="pl-smi"&gt;B&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;`&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-smi"&gt;R&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt; : &lt;span class="pl-smi"&gt;unknown&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T25&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;FirstTwoAndRest&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s"&gt;'abcde'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// ['ab', 'cde']&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T26&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;FirstTwoAndRest&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s"&gt;'ab'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// ['ab', '']&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T27&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;FirstTwoAndRest&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s"&gt;'a'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// unknown&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Template literal types can be combined with recursive conditional types to write &lt;code&gt;Join&lt;/code&gt; and &lt;code&gt;Split&lt;/code&gt; types that iterate over repeated patterns.&lt;/p&gt;
&lt;div class="highlight highlight-source-ts js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;Join&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;unknown&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-smi"&gt;D&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt;
    &lt;span class="pl-smi"&gt;T&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt; ? &lt;span class="pl-s"&gt;''&lt;/span&gt; :
    &lt;span class="pl-smi"&gt;T&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-smi"&gt;string&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-smi"&gt;number&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-smi"&gt;boolean&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-smi"&gt;bigint&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt; ? `${&lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-c1"&gt;0&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;` :
    &lt;span class="pl-smi"&gt;T&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-smi"&gt;string&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-smi"&gt;number&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-smi"&gt;boolean&lt;/span&gt; &lt;span class="pl-c1"&gt;|&lt;/span&gt; &lt;span class="pl-smi"&gt;bigint&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; ...infer &lt;span class="pl-smi"&gt;U&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt; ? `${&lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-c1"&gt;0&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;${&lt;span class="pl-smi"&gt;D&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;${&lt;span class="pl-smi"&gt;Join&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;U&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-smi"&gt;D&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;` :
    &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T30&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;Join&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-c1"&gt;1&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;2&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;3&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;4&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s"&gt;'.'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// '1.2.3.4'&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T31&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;Join&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-s"&gt;'foo'&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s"&gt;'bar'&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s"&gt;'baz'&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s"&gt;'-'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// 'foo-bar-baz'&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T32&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;Join&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s"&gt;'.'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// ''&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-ts js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;Split&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;S&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-smi"&gt;D&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt;
    &lt;span class="pl-smi"&gt;string&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;S&lt;/span&gt; ? &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt; :
    &lt;span class="pl-smi"&gt;S&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-s"&gt;''&lt;/span&gt; ? &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt; :
    &lt;span class="pl-smi"&gt;S&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; `${infer &lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;${&lt;span class="pl-smi"&gt;D&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;${infer &lt;span class="pl-smi"&gt;U&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;` ? &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; ...&lt;span class="pl-smi"&gt;Split&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;U&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-smi"&gt;D&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt; :
    &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-smi"&gt;S&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T40&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;Split&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s"&gt;'foo'&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s"&gt;'.'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// ['foo']&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T41&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;Split&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s"&gt;'foo.bar.baz'&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s"&gt;'.'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// ['foo', 'bar', 'baz']&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T42&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;Split&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-s"&gt;'foo.bar'&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s"&gt;''&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// ['f', 'o', 'o', '.', 'b', 'a', 'r']&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T43&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;Split&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;any&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s"&gt;'.'&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// string[]&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The recursive inference capabilities can for example be used to strongly type functions that access properties using "dotted paths", and pattern that is sometimes used in JavaScript frameworks.&lt;/p&gt;
&lt;div class="highlight highlight-source-ts js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;PropType&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-smi"&gt;Path&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt;
    &lt;span class="pl-smi"&gt;string&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;Path&lt;/span&gt; ? &lt;span class="pl-smi"&gt;unknown&lt;/span&gt; :
    &lt;span class="pl-smi"&gt;Path&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-k"&gt;keyof&lt;/span&gt; &lt;span class="pl-smi"&gt;T&lt;/span&gt; ? &lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-smi"&gt;Path&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt; :
    &lt;span class="pl-smi"&gt;Path&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; `${infer &lt;span class="pl-smi"&gt;K&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;.${infer &lt;span class="pl-smi"&gt;R&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;` ? &lt;span class="pl-smi"&gt;K&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-k"&gt;keyof&lt;/span&gt; &lt;span class="pl-smi"&gt;T&lt;/span&gt; ? &lt;span class="pl-smi"&gt;PropType&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-smi"&gt;K&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-smi"&gt;R&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt; : &lt;span class="pl-smi"&gt;unknown&lt;/span&gt; :
    &lt;span class="pl-smi"&gt;unknown&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-k"&gt;declare&lt;/span&gt; &lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-s1"&gt;getPropValue&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-smi"&gt;P&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;obj&lt;/span&gt;: &lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;path&lt;/span&gt;: &lt;span class="pl-smi"&gt;P&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;: &lt;span class="pl-smi"&gt;PropType&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-smi"&gt;P&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;declare&lt;/span&gt; &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;s&lt;/span&gt;: &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;obj&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-c1"&gt;a&lt;/span&gt;: &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-c1"&gt;b&lt;/span&gt;: &lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;span class="pl-c1"&gt;c&lt;/span&gt;: &lt;span class="pl-c1"&gt;42&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;d&lt;/span&gt;: &lt;span class="pl-s"&gt;'hello'&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-en"&gt;getPropValue&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;obj&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s"&gt;'a'&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// { b: {c: number, d: string } }&lt;/span&gt;
&lt;span class="pl-en"&gt;getPropValue&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;obj&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s"&gt;'a.b'&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// {c: number, d: string }&lt;/span&gt;
&lt;span class="pl-en"&gt;getPropValue&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;obj&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s"&gt;'a.b.d'&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// string&lt;/span&gt;
&lt;span class="pl-en"&gt;getPropValue&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;obj&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s"&gt;'a.b.x'&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// unknown&lt;/span&gt;
&lt;span class="pl-en"&gt;getPropValue&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;obj&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;s&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// unknown&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Mapped type &lt;code&gt;as&lt;/code&gt; clauses&lt;/h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;With this PR, mapped types support an optional &lt;code&gt;as&lt;/code&gt; clause through which a transformation of the generated property names can be specified:&lt;/p&gt;
&lt;div class="highlight highlight-source-ts js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-smi"&gt;P&lt;/span&gt; &lt;span class="pl-k"&gt;in&lt;/span&gt; &lt;span class="pl-smi"&gt;K&lt;/span&gt; &lt;span class="pl-k"&gt;as&lt;/span&gt; &lt;span class="pl-smi"&gt;N&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;: &lt;span class="pl-smi"&gt;X&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;where &lt;code&gt;N&lt;/code&gt; must be a type that is assignable to &lt;code&gt;string | number | symbol&lt;/code&gt;. Typically, &lt;code&gt;N&lt;/code&gt; is a type that transforms &lt;code&gt;P&lt;/code&gt;, such as a template literal type that uses &lt;code&gt;P&lt;/code&gt; in a placeholder. For example:&lt;/p&gt;
&lt;div class="highlight highlight-source-ts js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;Getters&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-smi"&gt;P&lt;/span&gt; &lt;span class="pl-k"&gt;in&lt;/span&gt; &lt;span class="pl-k"&gt;keyof&lt;/span&gt; &lt;span class="pl-smi"&gt;T&lt;/span&gt; &lt;span class="pl-c1"&gt;&amp;amp;&lt;/span&gt; &lt;span class="pl-smi"&gt;string&lt;/span&gt; &lt;span class="pl-k"&gt;as&lt;/span&gt; `get${&lt;span class="pl-smi"&gt;Capitalize&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;P&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;`&lt;span class="pl-kos"&gt;]&lt;/span&gt;: &lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt; &lt;span class="pl-c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-smi"&gt;P&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T50&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;Getters&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-c1"&gt;foo&lt;/span&gt;: &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;bar&lt;/span&gt;: &lt;span class="pl-smi"&gt;number&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// { getFoo: () =&amp;gt; string, getBar: () =&amp;gt; number }&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Above, a &lt;code&gt;keyof T &amp;amp; string&lt;/code&gt; intersection is required because &lt;code&gt;keyof T&lt;/code&gt; could contain &lt;code&gt;symbol&lt;/code&gt; types that cannot be transformed using template literal types.&lt;/p&gt;
&lt;p&gt;When the type specified in an &lt;code&gt;as&lt;/code&gt; clause resolves to &lt;code&gt;never&lt;/code&gt;, no property is generated for that key. Thus, an &lt;code&gt;as&lt;/code&gt; clause can be used as a filter:&lt;/p&gt;
&lt;div class="highlight highlight-source-ts js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;Methods&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-smi"&gt;P&lt;/span&gt; &lt;span class="pl-k"&gt;in&lt;/span&gt; &lt;span class="pl-k"&gt;keyof&lt;/span&gt; &lt;span class="pl-smi"&gt;T&lt;/span&gt; &lt;span class="pl-k"&gt;as&lt;/span&gt; &lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-smi"&gt;P&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;Function&lt;/span&gt; ? &lt;span class="pl-smi"&gt;P&lt;/span&gt; : &lt;span class="pl-smi"&gt;never&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;: &lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-smi"&gt;P&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T60&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;Methods&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-c1"&gt;foo&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;: &lt;span class="pl-smi"&gt;number&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;bar&lt;/span&gt;: &lt;span class="pl-smi"&gt;boolean&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// { foo(): number }&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;When the type specified in an &lt;code&gt;as&lt;/code&gt; clause resolves to a union of literal types, multiple properties with the same type are generated:&lt;/p&gt;
&lt;div class="highlight highlight-source-ts js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;DoubleProp&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-smi"&gt;P&lt;/span&gt; &lt;span class="pl-k"&gt;in&lt;/span&gt; &lt;span class="pl-k"&gt;keyof&lt;/span&gt; &lt;span class="pl-smi"&gt;T&lt;/span&gt; &lt;span class="pl-c1"&gt;&amp;amp;&lt;/span&gt; &lt;span class="pl-smi"&gt;string&lt;/span&gt; &lt;span class="pl-k"&gt;as&lt;/span&gt; `${&lt;span class="pl-smi"&gt;P&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;1` &lt;span class="pl-c1"&gt;|&lt;/span&gt; `${&lt;span class="pl-smi"&gt;P&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;2`&lt;span class="pl-kos"&gt;]&lt;/span&gt;: &lt;span class="pl-smi"&gt;T&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-smi"&gt;P&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt;
&lt;span class="pl-k"&gt;type&lt;/span&gt; &lt;span class="pl-smi"&gt;T70&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-smi"&gt;DoubleProp&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-c1"&gt;a&lt;/span&gt;: &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-c1"&gt;b&lt;/span&gt;: &lt;span class="pl-smi"&gt;number&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;  &lt;span class="pl-c"&gt;// { a1: string, a2: string, b1: number, b2: number }&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Fixes #12754.&lt;/p&gt;

&lt;p&gt;Playground: &lt;a href="https://www.typescriptlang.org/play?ts=4.1.0-pr-40336-88" rel="nofollow noopener noreferrer"&gt;https://www.typescriptlang.org/play?ts=4.1.0-pr-40336-88&lt;/a&gt;&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/microsoft/TypeScript/pull/40336" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Where already a start was made which works with only objects without arrays.&lt;/p&gt;

&lt;p&gt;But I want to go over step by step in how it works and how I extend it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Single Nested Object
&lt;/h3&gt;

&lt;p&gt;To make it easier to reason with generics, I will view them as functions. Example &lt;code&gt;type ResolveType&amp;lt;ObjectType extends object, Path extends string, OrElse&amp;gt;&lt;/code&gt; is function called &lt;code&gt;ResolveType&lt;/code&gt; which accepts 3 arguments. First argument should be of (sub)type &lt;code&gt;object&lt;/code&gt;, second argument must be a &lt;code&gt;string&lt;/code&gt; and the third argument is of type &lt;code&gt;any&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Our function must return type of the single level nested path or if it does not exists then return the third argument. In pseudo code it will look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function ResolveType(obj:Object, path:String, orElse:any) {
 if(path exists in obj) return type of obj[path]
 else return orElse;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To retrieve all the keys of a object we can use &lt;a href="https://www.typescriptlang.org/docs/handbook/2/keyof-types.html#the-keyof-type-operator" rel="noopener noreferrer"&gt;&lt;code&gt;keyof&lt;/code&gt;&lt;/a&gt; operator. To check if type &lt;code&gt;a&lt;/code&gt; is of type &lt;code&gt;b&lt;/code&gt; we can use the &lt;code&gt;extends&lt;/code&gt; operator. This covers our &lt;code&gt;if condition&lt;/code&gt;. The &lt;code&gt;if-else&lt;/code&gt; equivalent in generics are ternary statements. We then get the following result:&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ResolveType1&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&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;OrElse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Unlimited Nested Objects
&lt;/h3&gt;

&lt;p&gt;Now we must use the new &lt;a href="https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html" rel="noopener noreferrer"&gt;template literal types&lt;/a&gt; of Typescript, introduced in 4.1. This allows us, as I call it, to do pattern matching. Furthermore because nested objects can be nested more than 10 levels deep, we have to either loop or recursively call our generic type resolver. In 4.1 they also introduced &lt;a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-1.html#recursive-conditional-types" rel="noopener noreferrer"&gt;Recursive Conditional Types&lt;/a&gt; which allows us to recursively walk through all the nested levels of our object.&lt;/p&gt;

&lt;p&gt;Our pseudo code will look like:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function ResolveType(obj:Object, path:String, orElse:any) {
 if(path exists in obj) return type of obj[path]
 else if(path can be splitted between '.') {
   if(leftside of '.' is a key of obj) {
     return ResolveType(obj[leftSide], rightSide, orElse);
   }
 }
 return orElse;
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;First condition can be written as&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;Path extends `${string}.${string}`&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 But we don't want to just check if the path matches the pattern, but we also want to use the matched string. This can be achieved with the &lt;code&gt;infer&lt;/code&gt; keyword, which is not documented in detail in the handbook of Typescript. Our condition becomes&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;Path extends `${infer LeftSide}.${infer RightSide}`&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 The last condition we have to check is the same we already covered for single level nested objects.&lt;/p&gt;

&lt;p&gt;Typescript doesn't support a &lt;code&gt;AND&lt;/code&gt; operator in generics, so we have to used nested ternary.&lt;/p&gt;

&lt;p&gt;Our function becomes:&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ResolveType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&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;OrElse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;LeftSide&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;RightSide&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;LeftSide&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ResolveType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;LeftSide&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;RightSide&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Arrays
&lt;/h3&gt;

&lt;p&gt;The pattern of arrays are as followed: &lt;code&gt;nested[3]&lt;/code&gt; which is the simple variant. Our pseudo code becomes:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function ResolveType(obj:Object, path:String, orElse:any) {
 if(path exists in obj) return type of obj[path]
 else if(path can be splitted between '.') {
   if(leftside of '.' is a key of obj) {
     return ResolveType(obj[leftSide], rightSide, orElse);
   }
 } else if(path matches with pattern 'string.[number]') {
   if(leftside of [number] is a key of obj) {
     if(obj[leftside] is type of array) {
       return type of array
     }
   }
 }
 return orElse;
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Array notation can be pattern matched with &lt;code&gt;${infer LeftSide}[${number}]&lt;/code&gt;. We are just matching if a number is being used, since we can not infer the size of an array are compile time. If you use an index outside of the range you will get a Index Out of Bounds Exception, which is the only downside of the constructed generic. &lt;/p&gt;

&lt;p&gt;We can also use the &lt;code&gt;infer&lt;/code&gt; key word to infer the type of array we are using: &lt;code&gt;Array&amp;lt;infer U&amp;gt;&lt;/code&gt;. Typescript will then infer the type of the array. &lt;/p&gt;

&lt;p&gt;We then get the following function:&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ResolveType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&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;OrElse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;LeftSide&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;RightSide&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;LeftSide&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ResolveType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;LeftSide&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;RightSide&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;LeftSide&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;[&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="s2"&gt;]`&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;LeftSide&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;LeftSide&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;U&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;U&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Nested arrays
&lt;/h3&gt;

&lt;p&gt;We can now match the following constructions:&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="nx"&gt;expectType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nested.a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;expectType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;normal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;expectType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nested.a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;expectType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nested.b.c&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;expectType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;arr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;expectType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;arr[13]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;expectType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;anothernested.deep&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;


&lt;p&gt;I am using the following library for testing my types: &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/tsdjs" rel="noopener noreferrer"&gt;
        tsdjs
      &lt;/a&gt; / &lt;a href="https://github.com/tsdjs/tsd" rel="noopener noreferrer"&gt;
        tsd
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Check TypeScript type definitions
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;But we can not match for example &lt;code&gt;arr[13].length&lt;/code&gt; because we only check for the pattern &lt;code&gt;string[number]&lt;/code&gt;. We need to match for &lt;code&gt;string[number].string&lt;/code&gt; and infer the types. But &lt;code&gt;arr[13].length&lt;/code&gt; will match with our second condition in our pseudo code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if(path can be splitted between '.')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;So in our else statement of the second condition we have to check if it matches the pattern &lt;code&gt;string[number].string&lt;/code&gt;. And then recursively call our function with the right side of the dot. Our pseudo code becomes:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function ResolveType(obj:Object, path:String, orElse:any) {
 if(path exists in obj) return type of obj[path]
 else if(path can be splitted between '.') {
   if(leftside of '.' is a key of obj) {
     return ResolveType(obj[leftSide], rightSide, orElse);
   } else if(path contains pattern 'string[number].string') {
     if(leftSide of [number] is a key of obj) {
       if(obj[leftSide] is an array) {
         return ResolveType(type of array, rightSide, orElse)
       }
     }
   }
 } else if(path matches with pattern 'string.[number]') {
   if(leftSide of [number] is a key of obj) {
     if(obj[leftSide] is type of array) {
       return type of array
     }
   }
 }
 return orElse;
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;As you can see the added &lt;code&gt;if-else&lt;/code&gt; condition is similar to last one, except we recursively walk through the right side of the dot.&lt;/p&gt;

&lt;p&gt;Combining everything until now we get the final result:&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ResolveType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&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;OrElse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;LeftSide&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;RightSide&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;LeftSide&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ResolveType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;LeftSide&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;RightSide&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; 
    &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;LeftSide&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;[&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="s2"&gt;].&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;RightSide&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;LeftSide&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;LeftSide&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;U&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ResolveType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;U&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;RightSide&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;LeftSide&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;[&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="s2"&gt;]`&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;LeftSide&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;LeftSide&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;U&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;U&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrElse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Example &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Lets take the lodash get example and step by step walk through how the generic will be called and how it will resolve:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const barLength = get(obj,'foo.bar[3].length', -1);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We will assume that &lt;code&gt;obj&lt;/code&gt; is of type &lt;code&gt;FooBar&lt;/code&gt; and the path is valid. The generic will then be called with &lt;code&gt;ResolveType&amp;lt;FooBar, 'foo.bar[3].length', number)&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;foo.bar[333].length&lt;/code&gt; matches the pattern &lt;code&gt;string.string&lt;/code&gt;, Typescript will infer the following values &lt;code&gt;leftSide=foo&lt;/code&gt; and &lt;code&gt;rightSide=bar[333].length&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;foo&lt;/code&gt; is a key of &lt;code&gt;FooBar&lt;/code&gt;, lets assume that &lt;code&gt;FooBar['foo']&lt;/code&gt; is of type &lt;code&gt;Foo&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ResolveType&lt;/code&gt; will be called with &lt;code&gt;ResolveType&amp;lt;Foo, 'bar[3].length', number&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bar[3].length&lt;/code&gt; matches the pattern &lt;code&gt;string.string&lt;/code&gt;, Typescript will infer the following values &lt;code&gt;leftSide=bar[3]&lt;/code&gt; and &lt;code&gt;rightSide=length&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bar[3]&lt;/code&gt; is not a key of &lt;code&gt;Foo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bar[3].length&lt;/code&gt; matches the pattern &lt;code&gt;string[number].string&lt;/code&gt;, Typescript will infer the following values &lt;code&gt;leftSide=bar&lt;/code&gt; and &lt;code&gt;rightSide=length&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bar&lt;/code&gt; is a key of &lt;code&gt;Foo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Foo['bar']&lt;/code&gt; is an array of type &lt;code&gt;Bar&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ResolveType&lt;/code&gt; will be called with &lt;code&gt;ResolveType&amp;lt;Bar, 'length', number&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;length&lt;/code&gt; is a key of &lt;code&gt;bar&lt;/code&gt; since &lt;code&gt;bar&lt;/code&gt; is a &lt;code&gt;string&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ResolveType&lt;/code&gt; returns the type &lt;code&gt;number&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Future Work &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Publishing
&lt;/h3&gt;

&lt;p&gt;Currently you can copy the type from this article or from my repository:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Pouja" rel="noopener noreferrer"&gt;
        Pouja
      &lt;/a&gt; / &lt;a href="https://github.com/Pouja/typescript-deep-path-safe" rel="noopener noreferrer"&gt;
        typescript-deep-path-safe
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Collections of generics to make deep nested string paths type safe
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Type safe paths&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Collections of generics to make deep nested string paths type safe.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;ResolveType&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;It accepts 3 arguments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The object type&lt;/li&gt;
&lt;li&gt;The string path&lt;/li&gt;
&lt;li&gt;The type to return if it fails to resolve&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Usage&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;For example you can augment the lodash &lt;code&gt;get&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight highlight-source-ts notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt; &lt;span class="pl-s1"&gt;get&lt;/span&gt; &lt;span class="pl-k"&gt;as&lt;/span&gt; &lt;span class="pl-s1"&gt;_get&lt;/span&gt; &lt;span class="pl-kos"&gt;}&lt;/span&gt; &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;'lodash'&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;get&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;ObjecType&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;object&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-smi"&gt;Path&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-smi"&gt;OrElse&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;unknown&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;obj&lt;/span&gt;:&lt;span class="pl-smi"&gt;ObjecType&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;path&lt;/span&gt;: &lt;span class="pl-smi"&gt;Path&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;orElse&lt;/span&gt;?: &lt;span class="pl-smi"&gt;OrElse&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;: &lt;span class="pl-smi"&gt;ResolveType&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;ObjecType&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-smi"&gt;Path&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-smi"&gt;OrElse&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
  &lt;span class="pl-k"&gt;return&lt;/span&gt; &lt;span class="pl-en"&gt;_get&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;obj&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;path&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-s1"&gt;orElse&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-kos"&gt;}&lt;/span&gt;

&lt;span class="pl-en"&gt;get&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;obj&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;&lt;span class="pl-s"&gt;'deeplvl1[1].deeplvl2.deeplvl3[88].deeplvl4.value'&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Or for &lt;code&gt;react-final-form&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-source-ts notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;// file: form-value-hook.tsx&lt;/span&gt;
&lt;span class="pl-k"&gt;export&lt;/span&gt; &lt;span class="pl-k"&gt;function&lt;/span&gt; &lt;span class="pl-en"&gt;useFormValue&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;Path&lt;/span&gt; &lt;span class="pl-k"&gt;extends&lt;/span&gt; &lt;span class="pl-smi"&gt;string&lt;/span&gt;&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;field&lt;/span&gt;: &lt;span class="pl-smi"&gt;Path&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;: &lt;span class="pl-smi"&gt;ResolveType&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-smi"&gt;YourFormType&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-smi"&gt;Path&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-smi"&gt;unknown&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
  &lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;fieldInput&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-en"&gt;useField&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s1"&gt;field&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt; &lt;span class="pl-kos"&gt;{&lt;/span&gt;
    &lt;span class="pl-c1"&gt;subscription&lt;/span&gt;: &lt;span class="pl-kos"&gt;{&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Pouja/typescript-deep-path-safe" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;I hope in the near future to publish it to NPM so you can install it (when I figure out how).&lt;/p&gt;

&lt;h3&gt;
  
  
  Autocomplete
&lt;/h3&gt;

&lt;p&gt;I want to look into making generic type where is accepts variable list of function arguments to determine the type. This allows you to use auto completion of typescript itself.&lt;br&gt;
The end result should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;get(obj, 'orElse', 'lvl1path', 3, 'lvl2path', '[your cursor]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where in the last argument you will get the autocomplete of Typescript for the type &lt;code&gt;obj.lvl1path[3].lvl2path&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>generics</category>
      <category>safety</category>
      <category>lodash</category>
    </item>
  </channel>
</rss>
