<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.1.1">Jekyll</generator><link href="https://playest.net/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://playest.net/blog/" rel="alternate" type="text/html" hreflang="en" /><updated>2024-09-06T21:15:38+02:00</updated><id>https://playest.net/blog/feed.xml</id><title type="html">Playest’s blog</title><subtitle>A blog about stuffs.</subtitle><entry xml:lang="en"><title type="html">Write the Tools You Need for Code you Want to Type</title><link href="https://playest.net/blog/2021/11/28/Write-the-Tools-You-Need-for-Code-you-Want-to-Type.html" rel="alternate" type="text/html" title="Write the Tools You Need for Code you Want to Type" /><published>2021-11-28T00:00:00+01:00</published><updated>2021-11-28T00:00:00+01:00</updated><id>https://playest.net/blog/2021/11/28/Write%20the%20Tools%20You%20Need%20for%20Code%20you%20Want%20to%20Type</id><content type="html" xml:base="https://playest.net/blog/2021/11/28/Write-the-Tools-You-Need-for-Code-you-Want-to-Type.html"><![CDATA[<p>“Write the tools you need for code you want to type.”</p>

<p>This is the best way I found to formulate how I choose to write my code. Or maybe “Write the tools you need for code you want to <strong>read</strong>.”, or “write your code as a library”? I don’t know which on is better…</p>

<p>I will try to explain with an example.</p>

<h2 id="the-example">The example</h2>

<p>When you code you usually have a set of pre-existing tools (methods, classes, functions, …) that you can use to accomplish a task. Sometimes it comes from some kind of standard library, sometimes it comes from the code you already written and sometimes it comes from third-party library that you imported. Wherever these tools come from doesn’t really matter.</p>

<p>Let’s say you have a set of function at your disposal:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">declare</span> <span class="kd">function</span> <span class="nx">querySelector</span><span class="p">(</span><span class="nx">selector</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="nx">HTMLElement</span> <span class="o">|</span> <span class="kc">null</span><span class="p">;</span>
<span class="kr">declare</span> <span class="kd">function</span> <span class="nx">addEventListener</span><span class="o">&lt;</span><span class="nx">Evt</span><span class="o">&gt;</span><span class="p">(</span><span class="nx">eventName</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">callback</span><span class="p">:</span> <span class="p">(</span><span class="nx">e</span><span class="p">:</span> <span class="nx">Evt</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="k">void</span><span class="p">):</span> <span class="k">void</span><span class="p">;</span>

<span class="kr">interface</span> <span class="nx">UserConnectionStatusChangedEvent</span> <span class="kd">extends</span> <span class="nx">Event</span> <span class="p">{</span>
  <span class="nl">status</span><span class="p">:</span> <span class="dl">"</span><span class="s2">loggedin</span><span class="dl">"</span> <span class="o">|</span> <span class="dl">"</span><span class="s2">loggedout</span><span class="dl">"</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>So when smeone asks you hide or show/hide the <code class="language-plaintext highlighter-rouge">#login-button</code> and <code class="language-plaintext highlighter-rouge">#logout-button</code> when the event <code class="language-plaintext highlighter-rouge">user-connection-status-changed</code> is fired, you write:</p>

<figure class="highlight">
  <pre><code class="language-typescript" data-lang="typescript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
</pre></td><td class="code"><pre><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">user-connection-status-changed</span><span class="dl">"</span><span class="p">,</span> <span class="p">(</span><span class="nx">e</span><span class="p">:</span> <span class="nx">UserConnectionStatusChangedEvent</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">loginButton</span> <span class="o">=</span> <span class="nx">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">#login-button</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">logoutButton</span> <span class="o">=</span> <span class="nx">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">#logout-button</span><span class="dl">"</span><span class="p">);</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">loginButton</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="nx">logoutButton</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">"</span><span class="s2">Could not find login-button or logout-button</span><span class="dl">"</span><span class="p">);</span>
    <span class="k">return</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">status</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">loggedin</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// hide the login button and show the logout button</span>
    <span class="nx">loginButton</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">none</span><span class="dl">"</span><span class="p">;</span>
    <span class="nx">logoutButton</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">inline</span><span class="dl">"</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">status</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">loggedout</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// show the login button and hide the logout button</span>
    <span class="nx">loginButton</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">inline</span><span class="dl">"</span><span class="p">;</span>
    <span class="nx">logoutButton</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">none</span><span class="dl">"</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">});</span>
</pre></td></tr></tbody></table></code></pre>
</figure>

<p>This code works. But is it the code you would like to write? Is it the code you would like to read? What about:</p>

<figure class="highlight">
  <pre><code class="language-typescript" data-lang="typescript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
</pre></td><td class="code"><pre><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">user-connection-status-changed</span><span class="dl">"</span><span class="p">,</span> <span class="p">(</span><span class="nx">e</span><span class="p">:</span> <span class="nx">UserConnectionStatusChangedEvent</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">loginForm</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">LoginForm</span><span class="p">(</span><span class="dl">"</span><span class="s2">#login-form</span><span class="dl">"</span><span class="p">);</span>
  <span class="nx">loginForm</span><span class="p">.</span><span class="nx">loginButton</span><span class="p">.</span><span class="nx">setVisibility</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">status</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">loggedin</span><span class="dl">"</span><span class="p">);</span>
  <span class="nx">loginForm</span><span class="p">.</span><span class="nx">logoutButton</span><span class="p">.</span><span class="nx">setVisibility</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">status</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">loggedout</span><span class="dl">"</span><span class="p">);</span>
<span class="p">});</span>

<span class="c1">// For which you would need to write the following tools:</span>
<span class="kd">class</span> <span class="nx">LoginForm</span> <span class="p">{</span>
  <span class="k">private</span> <span class="nx">element</span><span class="p">:</span> <span class="nx">HTMLElement</span><span class="p">;</span>
  <span class="k">public</span> <span class="kd">constructor</span><span class="p">(</span><span class="nx">selector</span><span class="p">:</span> <span class="kr">string</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">element</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="o">&lt;</span><span class="nx">HTMLElement</span><span class="o">&gt;</span><span class="p">(</span><span class="nx">selector</span><span class="p">);</span>
    <span class="k">if</span><span class="p">(</span><span class="nx">element</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">"</span><span class="s2">Unrecoverable error: could not find login form in page</span><span class="dl">"</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">element</span> <span class="o">=</span> <span class="nx">element</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="kd">get</span> <span class="nx">loginButton</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">wrapElement</span><span class="p">(</span><span class="dl">"</span><span class="s2">.login-btn</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="kd">get</span> <span class="nx">logoutButton</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">wrapElement</span><span class="p">(</span><span class="dl">"</span><span class="s2">.logout-btn</span><span class="dl">"</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="k">private</span> <span class="nx">wrapElement</span><span class="p">(</span><span class="nx">selector</span><span class="p">:</span> <span class="kr">string</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">b</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">element</span><span class="p">.</span><span class="nx">querySelector</span><span class="o">&lt;</span><span class="nx">HTMLElement</span><span class="o">&gt;</span><span class="p">(</span><span class="nx">selector</span><span class="p">);</span>
    <span class="nx">assert</span><span class="p">(</span><span class="nx">b</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Could not find </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">selector</span><span class="p">);</span>
    <span class="k">return</span> <span class="k">new</span> <span class="nx">HtmlElementWrapper</span><span class="p">(</span><span class="nx">b</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nx">assert</span><span class="p">(</span><span class="nx">cond</span><span class="p">:</span> <span class="nx">boolean</span><span class="p">,</span> <span class="nx">message</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="nx">asserts</span> <span class="nx">cond</span> <span class="p">{</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">cond</span> <span class="o">==</span> <span class="kc">false</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">"</span><span class="s2">Unrecoverable error: </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">message</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="nx">HtmlElementWrapper</span> <span class="p">{</span>
  <span class="k">public</span> <span class="kd">constructor</span><span class="p">(</span><span class="k">private</span> <span class="nx">element</span><span class="p">:</span> <span class="nx">HTMLElement</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
  <span class="k">public</span> <span class="nx">setVisibility</span><span class="p">(</span><span class="nx">visibility</span><span class="p">:</span> <span class="nx">boolean</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span><span class="p">(</span><span class="nx">visibility</span> <span class="o">===</span> <span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">element</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">inline</span><span class="dl">"</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">else</span> <span class="p">{</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">element</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">none</span><span class="dl">"</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre>
</figure>

<p>In my opinion the second <code class="language-plaintext highlighter-rouge">addEventListener</code> call has a much lighter mental load. But I have to admit that the second solution need a lot of <em>tools</em> to work.</p>

<p>The first solution is 18 lines long and the second is 49 lines long. So from a line count point of view it becomes interesting as soon as we write the same kind of code three times.</p>

<p>Let’s say that later you have to hide the login button when in fullscreen mode, and when you preview a file. With the first solution you need to write 27 more lines:</p>

<figure class="highlight">
  <pre><code class="language-typescript" data-lang="typescript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
</pre></td><td class="code"><pre><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">fullscreen</span><span class="dl">"</span><span class="p">,</span> <span class="p">(</span><span class="nx">e</span><span class="p">:</span> <span class="kr">any</span> <span class="cm">/* any for convenience, should be something like FullScreenEvent */</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">loginButton</span> <span class="o">=</span> <span class="nx">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">#login-button</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">logoutButton</span> <span class="o">=</span> <span class="nx">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">#logout-button</span><span class="dl">"</span><span class="p">);</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">loginButton</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="nx">logoutButton</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">"</span><span class="s2">Could not find login-button or logout-button</span><span class="dl">"</span><span class="p">);</span>
    <span class="k">return</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">fullscreen</span> <span class="o">==</span> <span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">loginButton</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">none</span><span class="dl">"</span><span class="p">;</span>
    <span class="nx">logoutButton</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">none</span><span class="dl">"</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="p">{</span>
    <span class="nx">loginButton</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">inline</span><span class="dl">"</span><span class="p">;</span>
    <span class="nx">logoutButton</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">inline</span><span class="dl">"</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">});</span>

<span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">enter-file-preview</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">loginButton</span> <span class="o">=</span> <span class="nx">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">#login-button</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">const</span> <span class="nx">logoutButton</span> <span class="o">=</span> <span class="nx">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">#logout-button</span><span class="dl">"</span><span class="p">);</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">loginButton</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="nx">logoutButton</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">"</span><span class="s2">Could not find login-button or logout-button</span><span class="dl">"</span><span class="p">);</span>
    <span class="k">return</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="nx">loginButton</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">none</span><span class="dl">"</span><span class="p">;</span>
  <span class="nx">logoutButton</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">none</span><span class="dl">"</span><span class="p">;</span>
<span class="p">});</span>
</pre></td></tr></tbody></table></code></pre>
</figure>

<p>With the second solution you would need to write 11 more lines:</p>

<figure class="highlight">
  <pre><code class="language-typescript" data-lang="typescript"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">fullscreen</span><span class="dl">"</span><span class="p">,</span> <span class="p">(</span><span class="nx">e</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">loginForm</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">LoginForm</span><span class="p">(</span><span class="dl">"</span><span class="s2">#login-form</span><span class="dl">"</span><span class="p">);</span>
  <span class="nx">loginForm</span><span class="p">.</span><span class="nx">loginButton</span><span class="p">.</span><span class="nx">setVisibility</span><span class="p">(</span><span class="o">!</span><span class="nx">e</span><span class="p">.</span><span class="nx">fullscreen</span><span class="p">);</span>
  <span class="nx">loginForm</span><span class="p">.</span><span class="nx">logoutButton</span><span class="p">.</span><span class="nx">setVisibility</span><span class="p">(</span><span class="o">!</span><span class="nx">e</span><span class="p">.</span><span class="nx">fullscreen</span><span class="p">);</span>
<span class="p">});</span>

<span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">enter-file-preview</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">loginForm</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">LoginForm</span><span class="p">(</span><span class="dl">"</span><span class="s2">#login-form</span><span class="dl">"</span><span class="p">);</span>
  <span class="nx">loginForm</span><span class="p">.</span><span class="nx">loginButton</span><span class="p">.</span><span class="nx">setVisibility</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span>
  <span class="nx">loginForm</span><span class="p">.</span><span class="nx">logoutButton</span><span class="p">.</span><span class="nx">setVisibility</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span>
<span class="p">});</span>
</pre></td></tr></tbody></table></code></pre>
</figure>

<p>But the second solution is also much more error resistant. You probably won’t make copy/paste mistakes for example. If you decide on a fallback method for when the buttons can’t be found you’ll just have to update it in one place. You could also imagining adding methods to the LoginForm and HtmlElementWrapper classes as your project grows.</p>

<h2 id="get-on-with-it">Get on with it!</h2>

<p>This kind of thinking is, I think, related to the <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a> principle in the way that as soon as you start writing the code for the “fullscreen” event and “enter-file-preview” the second solution becomes kind of obviously better but as long as you limit yourself to the “user-connection-status-changed” it’s less straightforward.</p>

<p>So maybe we should talk about Anticipated-DRY? Because we want to write code like if you were going to repeat it even when you’re not (yet).</p>

<p>(Now that I have written this it would be fun to call this <em>CRY</em> for “Can’t Repeat Yourself”.)</p>

<p>Obviously, as with many things, this is not absolute, and sometimes repeating yourself is the way to go.</p>

<p>I’m not sure I was able to convey my point in this article. Maybe all of that is obvious. Maybe it’s just rambling.</p>]]></content><author><name></name></author><category term="programming" /><category term="en" /><summary type="html"><![CDATA[“Write the tools you need for code you want to type.”]]></summary></entry><entry xml:lang="en"><title type="html">Not Vancian Magic in D&amp;amp;D5</title><link href="https://playest.net/blog/2021/11/14/Not-Vancian-Magic-in-DnD5.html" rel="alternate" type="text/html" title="Not Vancian Magic in D&amp;amp;D5" /><published>2021-11-14T00:00:00+01:00</published><updated>2021-11-14T00:00:00+01:00</updated><id>https://playest.net/blog/2021/11/14/Not%20Vancian%20Magic%20in%20DnD5</id><content type="html" xml:base="https://playest.net/blog/2021/11/14/Not-Vancian-Magic-in-DnD5.html"><![CDATA[<p>When I started playing D&amp;D5 I had difficulties understanding the different magical archetypes of the game. Probably because I played a little D&amp;D3 and 3.5 a few years ago and it muddied the water. Now that I am used to it it’s not that complicated by I find that formalizing stuff usually helps me so here we go!</p>

<h2 id="vancian-magic-a-bad-history">Vancian Magic: a Bad History</h2>

<p>A long time ago there was <em>Vancian</em> magic, it comes from some writer named Vance I think. Vancian spells are alive in the mind of the caster, they have to prepare each spell when they wake up. I read somewhere that the read the whole incatation except the last few words, the spell is almost finished, it’s just waiting for the last words to be released! These last words are the magical words that trigged the spell in battle (think “<a href="https://www.pokemon.com/us/pokedex/abra">abra</a><a href="https://www.pokemon.com/us/pokedex/kadabra">kadabra</a>”). This vancian magic was used in older edition of D&amp;D but it’s not anymore because it wasn’t flexible at all. You prepared exactly the spell you were going to cast so if you had 3 first level slots and 2 second level slot you may have prepared:</p>
<ul>
  <li>2 command spells</li>
  <li>1 bless spell</li>
  <li>1 spiritual weapon spell</li>
  <li>1 lesser restoration spell</li>
</ul>

<p>During the day if you realized that you didn’t need your spiritual weapon and would like to replace it with lesser restoration it wasn’t possible. I think they were some exception, like the Cleric was able to replace any vancianly prepared spell with a heal spell but the flexibility was very low.</p>

<h2 id="pseudo-vancian-magic">Pseudo-Vancian Magic</h2>

<p>In D&amp;D5 they replace this with pseudo-vancian magic. The idea is that you have a pool of spell that you can pick from when you use a spell slot to cast a spell. The flexibility of this pool depend on your class.</p>

<p>Some classes <strong>learn new spells</strong> after certain events (usually when they level up) and their pool contains all the spell they know so they don’t need to <em>prepare</em> spells (alternatively you could say that they have all of their spells prepared all the time). The Bard, Sorcerer, Ranger, Rogue Arcane Trickster, Warlock works like this. They all have a “known spell” column in their table in the books. I call them <strong>school-of-life</strong> magicians.</p>

<p>Some classes <strong>know all of their spell</strong> from the start and their pool contains a subset of their choice that they can change after every long rest, that’s when they <em>prepare</em> their spells. The Cleric, Druid and Paladin work like this. I call them <strong>know-it-all</strong> magicians.</p>

<p>You will notice that Wizards are not mentionned, that’s because they are a weird case.</p>

<h2 id="the-case-of-the-wizard">The case of the Wizard</h2>

<p>The Wizard is a weird case. They learn new spell when they level up but also when they find scroll or spellbooks.</p>

<blockquote>
  <p>You might find other spells during your adventures. You could discover a spell recorded on a scroll in an evil wizard’s chest, for example, or in a dusty tome in an ancient library</p>
</blockquote>

<p>Technically there is no “known spell” column in the PHB table but the text says (page 114):</p>
<blockquote>
  <p>At 1st level, you have a spellbook containing six 1st-level wizard spells of your choice.</p>
</blockquote>

<p>and</p>

<blockquote>
  <p>Each time you gain a wizard level, you can add two wizard spells of your choice to your spellbook.</p>
</blockquote>

<p>That means that even if wizards don’t automatically know all of their spell, they could learn a lot of them (and it’s the class that has access to the most spells) so for balance reason they can’t have <em>“all of their spells prepared all the time”</em>. That’s why they have to prepare a subset of their spells after every long rest.</p>

<p>In conclusion Wizard are hybrids. I call them <strong>learn-it-all</strong>.</p>]]></content><author><name></name></author><category term="dnd" /><category term="dnd5" /><category term="ttrpg" /><category term="en" /><summary type="html"><![CDATA[When I started playing D&amp;D5 I had difficulties understanding the different magical archetypes of the game. Probably because I played a little D&amp;D3 and 3.5 a few years ago and it muddied the water. Now that I am used to it it’s not that complicated by I find that formalizing stuff usually helps me so here we go!]]></summary></entry><entry xml:lang="en"><title type="html">Mapped Types in TypeScript</title><link href="https://playest.net/blog/2021/11/13/Mapped-Types-in-TypeScript.html" rel="alternate" type="text/html" title="Mapped Types in TypeScript" /><published>2021-11-13T00:00:00+01:00</published><updated>2021-11-13T00:00:00+01:00</updated><id>https://playest.net/blog/2021/11/13/Mapped%20Types%20in%20TypeScript</id><content type="html" xml:base="https://playest.net/blog/2021/11/13/Mapped-Types-in-TypeScript.html"><![CDATA[<p><em>(You can also read the whole article on the <a href="https://www.typescriptlang.org/play?#code/PQKgsAUAMgpgLgcgM4AIkEMCeKDuMUAW6AbvuigCbpzoBG6S+OAlnASugE5zMDGANjFToAdhRQBXRpyQA6FAHUmouCjgB7XJ1b4k6gLb4AZhJG8e6kWs0BzeCh37URzgbUF8VGvUazIkWERUChgjZhEmIlUuMm4+QWErKRgZFDCZOD8IEGB-CHC4FKN0XnwAQTiBfABvSBQ1VkEALjQ4bREbABo6lF5LQpE4FqQ28K6erxgWkQl9WhTuiHqCZgoQkRbadXVBUR7+URsWgCIYEWOUAB8UY5cL69OkY8gAXzyCopL8AFVpFFqlih+OobOFhqMOotlgwPBQAAoMJA4dScCjg9rjQEYfhDVoYqGUIS8bQABwsGzxY0WbwgkFAAXgyF6nBg1F0BnwyVSonEXB4VTkkByeT6IhGkmkSBavxSAG0ALooAC8KFlPWqQJBYJuZX4fBgx06hBhMHhiORqJO6ENaHQOKtBBtISQxOYZOYlhOAAlmEaAJIcfQoXX6jhidzMVCRlD6bAklJ6ER2tAkr6yC4vAka4GginHABC6loNqISFhCKQSJRaJuxaN2Nxx1ojqNztd7s9Ny9MH4wP9gZQhdo6ZQmfVmtzJwAwlwdiWTWbKxaa8deDaGydeC3CS7SeTvb6UAH0EGQ6Uw+I2NHo7GUPGZJZk0hU6UR2OIPKANwiyzivnxIQWgqflBAVZVVXHHg4GaLse2BG1RQGRsAHFtgoWhMBgdNWzZFoAEYjRWNYzhaYp+EYI0Dg6E4zgzLMGmgqYblQ9RxH0FERDGBD+jORsvXQEkSWwdAjEKTgRG2ERsMoXCUAAJkI1Z1haNoJBgSjDho85R3oqCYOOL0dmtI1EN4k4AH6AEU1LUO0AH4nVkgBmRTiIpMiKKBTSbiEOjIHlPJ6QgMpw0EIIY2YAAPDhexQdQjHcagHCsUQOE4TgsES1QWFikpolQdQJE4GTvAYLChVyWkIFFP9zAkO0ABFaBaAAKGViuuYCAIASjAlU1UBLkkFlAAGeUjWAYAUBGoC9VKHp-wFUbxpQSaUDwlpu17dQeiG2U8JWta5JaIcFsqBJZTkw6pqczajLOkChH266UAAFhaFi2I4sZdslS6XoAVhaGdXH4fzAvACAADl1BwXB8F4VKcG0QoODSUxzA9Kw2CSuw4ESBxCiDFw3DYTxqDoMqsmFKqTDMckUDxgAxVx9CavCWtWaZZnmThutWkBkqMFIWXEFk4CK7HMHjBxUHaq5g3O-Acn+Hpxcljg6sa2hZVWL9XghyB8wkbKogcBKyZZWWUAkwmYCDcJ3HJ0rGFwVh2DJhxxDwO9EVNDhhF9jLDDEuyUAAA2Z1n2Za165O68PcEK-gxfgDXTBCMIInEWgTadlB1fEtRpfweL87pzHLEodQhBEBBVAwbAM9CcJTXkAAVDwrejOBkVwTKNEZzQ6EK1Qr0FCA8PkKciA6fB25LgBldtVFFMIbCK6gsesFAzjoQRVQk74zA8XgAGtTT9MQYEi00yl4UpK3lFqCDgOASSlSacG-2Q4BL3c3TQUOLIFENhgD4zXswGwABiI+J8YDn0vtfW+FB76PyQPzHAKwty2kwKgQens+j6BJMwQQxUcaqBgKQTgwkH5CHwcPFK6VMrhBCNFPoEgU4FzTkXcOzcs6mnDkaVgSdOFsXQBffOhcpYyzLpHeALMDDs0TuEYW6V-YMAjvLTqSsFb8NbhQcOHcEEEE4ojXsmBhGqB7pwGyFDd7UOwKwm+Gi6GVh3qlLgGVsCIysNIyQ18BE5zziI-QmV5goAAI4SB0L0HmCYOTm0ZmcFIdpmAAC9-YEI8LgAgOwEasRgD4dSaQUS70iieEkghhEJUwIVXeSZaAHxEXUiQCBYqMHwPYz2IQBBcC3lXMueV6r8CasaAmIsylJSoSkbAeoIhZDktPWedh85bAoNgMunsK7knrDAfA8i4CKLZrQOSid5jAhwBVPIOzt5RyUaczmNYZhzBSPzUAQsRb+38X-GW0ZtGK0egrGYsUVYAnqMwBKTyUAAD4VTDO1rIQQHQ2D83BfUbhEsi4gv4J+VaU0fa9NWF0zQ-icXJRGKycQZd9HZ0ICkGAPQaT1H8Qi0ZOs9bfggDSOkkNO7XlQPoCQOD5hvxSMlGMmySGcSrkgcI54RFsv4NgIwKJH4Sh3luBBZ8kmex7PbXi1slVOOxjkrwlNfDXKqmFO2+gNooABV1Ko4F7knLki1eOeK1qfSNIQgwJCyGM2YKQMMu90plK7vgY4C8ZYICdQfa4AKcUIGthJfKcqbCNIPgQkuKA426MTdIBAHdc0IGTam9Q6aoFZpJcXWN8b8CFpSAgZ4EAbWOGOg6v4OigXXHJSqV1TV3WevxSgflqALRnyQEaH2N8ql8EaNgRu+CzYdpjOE-AOLIDtqJk5AWnz1GXlzf87tgKALAs4fwFAKsB0KOjo8kda0ADytBiAeikMqlArTq511UBELJmg51zUXVoWJntfklJjTAZee4jVNNrfeYW5hP2qPFawbd9hHDvS7eKnt57b1HPvRzR9U1baTOKiwNg+cSQsjfYVVA1C5WWBndg9g0Yz6sLiglKgc9XBSBQPMRGyRCa9FSi1Hkd5XD0CadgHKV6kp6EMHedQBR+b+L4YEgx4dDYQGgdAlAyF7CWHwI4LjaNnwIMhXwOtjKqowzhmFJkjcspfvqTgFQO8wmSPILcqug88Zo3lhhQmUYKDyAAJr1I4VwigmhFPwBWB0IEzAL5WsgL5qweN2r5kwH6CgHMuY23iXzVWgJ-GDseasbqXqCWcldoVYq8xxSMe3mXCrBsqpBT9NlZO3sUQ6s0cCJLmjWkICtqmSsYxQ2rBc9GEmQZPFayve1eQxsx4rGcBjBm0XeTkVJTwlKZ7nUtU9qhw9mKNYQbM1l6QOW8t4WtuHR1Baiu9nDt1FjfA2OoBo0Wfe2A025KSj7dzgwUCc3CkmQw4hjg3ZSBcESYkYr8G6tTSq6Wtt3PgNl3LFB3WFZebzd5gszuiwu0XK7J7cOvavWCnoNVrFExdXeh5+OKDVZ6JC8HpmlQqhxWinoLKDs065fUZlu9yImahccHM4QLiO0cALwE9Q1rditlBmDgCUBnwkjgFdSVTPRnIEFhB6BhMiNLGjWXVgwg9goEaCToq8BnBw8VCTDa4oiE-fLS35BrdpGYHbwX5OrCOFF6ONWwucU1dyS7w3qBAfG7+D7Mll6jSA6FTgn2vjq70pZL6nJ5jA2-ek5+sJIQ0b6EjDQC+M6CliPcK4OGqUb6lA7FYXOmRZDyBap0jgolxUjzzq3mA7eMEdd5QycKsX84YCU1eJLqrioNrRzczHVc8YNru3jp53NXklY+aT75wvKeoA932y916prooZ7a5nhHWdPI54CLnEP7bKj55epXGKQ8i6ZT0HsV2V-Y4PSA0CVRXUrH-fxMPf-MrKPS9LlHlCAIKT6eQEKcQXXIERkVAZzEHDzHzdfU1JKHPYWOAHBUQJxJnebfOc1YpEyU+HVERVgJkLZHJbQGwV+GzcHeWMuBtfmCTY1X-ERSwFDBKZggqM+eQReDkBfGwZLC+JoNLCADLZJOAEKXLImE7EuK0JWe4G4LkG0AnYrYnA9MnH5Y9OWU9c-GnK-SA3oX8Rnd-AjY5IdJ-cPV-HnT-Xsb-DFVPXscPcXQAqXcHK7XnG4RaQQC4AAMiiJuFAPl1DyJh8KFyxUSPtgCIAMlySS0JljCOOAMJQBiJuGtwSNtWSKEKJgyMBCCLsJSI1mj1gKQK61UGBHUCnSHlYnkAAFFIo291sDdIdK0UBHFJM8o+BkxB5khV9aZCDVC8tjl2oWofC1ofZhNyAMoxA3A7AIgMoNBipI0BM85TsQhyAe4zZPYuRrZfkptjiBNsBZ5xA9ARMdsBMyARBMBZDI9UiUAABZagAgWQPjMQFqP4tgQEnkAwJY69daEaarUdcgQncVJ3fZKwEaC8FAAATgnzbXsHIEPxEDUTJ1P1dwVmsPJRvVUPUO63thamOAiINCNDwjhJWNISvVZV0S2mBAw1UFoH3SPyPT+UsOpzJMvwpM33eOpP0FpIMKNFhJjzk1-0uLPFsxtV4D5IJK+QFJMyFI6iOwTRsLFPgCpM0LpN0KNDxnmNZkWO6mZIJVZN-2MzM09npLimKi3RxNUHEHxMJKyQsJJLw2dQv1BSmgI2NJpPyOkBtAtIoAWOkCWNtLdlin8UdNYLq3FTKXdMnwgCUBQAAGkoYn0FAXNw5eBE4FSIkCz25XinTc0VsjxV5RB65XihMgCxDwp4tZCJdXZCEGCI5QiVQIz4dCjYiZctRtIFciYyz7SIkPI3jLwckVCFS-D+AshIAr5Z8TxlBZN8B8zCyXNxApwygoZKzBwuiayZYV88goYZlyEckEgV0ul-4zMSzE4JNw5DE0oDlnte0acjF6zREuEIkzFgjBMzd6sLYFzmAMgg5Nykd5gptTTHoLgQcGUOB4Md5ChYoNcV4XNPZzCZZ3NUAbAVMktB4IlUoV8UBF5NyIwkssEXdtkoLxRUxg54A0NUBBzOBjhVyIAujBhxUwlBJfT4wkAABCPIPTX4gSeMcQKDJAJQj4TgYoc8JqF9AAKwQTgD+MEim3RUQoAmOCAiVgJE4sMtd2pDX3pixzUIlKJjkgAB4qyb4BgKBUAL46kEpVLaANLzBtLpUbAYUcimJ25hFnljCWgvKfKtLpKxhZR25FQgyr10U1pHNUBdgQ1PZ1lsB7YyQVUMzYYvj6jL1RKY81KpBohjRz4d4WR2IMqcl3NxIptqDPZsLYMiEA0FgHBM0UQTM4BsSgoyg0gYA4YgNRABkxRPzJM-sZMbYEF6EuBMB5AmYmLTYkpYshAI53Ky5IrNK-KxhE5DBRAkA7IlCbUmItrPLaB1LdqYqksBz6TjgY82jIB8AByDCnqz4XrwJjgPBtpHrR1yMWgoM80fq4J1BjgU1oxE9JtM195a0rsEALqUAdrfLbqbBi0dNpDNqYAPLkarrvKbqdKOgVFUAfBxiLEI59Kqg9DTKhFqN1BJt0LiA7Q1J9d1tlYQB3KCojAQBBYUboqia5CXiiBSAO4ckubJrPYocAN85BBRInSclDLSj+a9qOgeLF4EFLB7cgdPSa5UBw4nLIoXK3Kcbtr8aorVabADrWQxQw5usYwba2b4ZCQ+krZyArtmbtA4bRNex-Yqz7EhVxQIktlc1w4kaVa0bE4niYB3ZxUqbIjXT9DIyxaDkINE4WLYKBLyrqzWCe5c0niREc8RDsAIl46wCylTKeL+VUQWMkoh9VBw4I7Ba4r5Rw4w4lBYpMD7EfYddYYXMqzoxw4y6aaDCVFVBolYkiw6MpB5BG7zbCb-KW6pzYoIkntpAyyPArADaSbKbR70Tw4G0N6Xdt7Hth6jE8hAgnN9lCA+7s9UphMKFpi6QpoarnFOAWhRpZp9RGTNowajRO0hwjRboUBDIDgjRsNPoYxvpIQUAgYUAQYdgAoPTJAHtvTNSuCqddTySQzKTbL7YHLTLArTLZSEyyxetFS-hlTuSOBUGScNTztiSRTgz78wz9ACGHqiGHrGTSG8kG92SgVOSdpkH0A5J1SfStTrYmHaccHxSND8H7Lh7OGzT5IeHyHly0B5UukckZtKM2NxBRGjcSSJMfYYgA4ONF8pkrAG0dMCw84TGrYyrxRkZWAEKCje59RRL26ogmRPYSQSK4A7JRKLhv0wlhIkBJDgxwxv1TH2DX55B+BhUONP0l8JQjRWqtdowkAwluAjRv0c9HHVAar1A6rjAVqKUaAzA5yRMFSQgKBhUtGFNNHqN+kQ5xVw407rZTLwcK6Hr+YyYrAqzA6eSo0CiWoendDUdTr7AJA900GGG-Sk1RSZGjS8G2HpTIySGY9wM-T+SA5cG5G2GFGDDAqIMTgZSvY98idpRT13SbV0BZm6HxGMGz8XtsGWHVn3Vh7uGtmcliTdnNFZHJT2HdCTntDwjlGjD99uojKfzMzkDIZ7MsDwo2hsBB5nHUZMrWJNkEoGt0YrLLAn7lDZjAXd1HLyljbtdTbLrrrUbBbQX4wgbQrLm3kIr57aXF74qL1Yob8HC79nD702dn8IUjA38gwwj+dajR0YY79dGIxQtxlXiXdVUM5g9lyqj6gajX9+zwWkLhy4jGgwCJz7Zyi1p0myRtddcnb49Q0PdI1VXhcYDlcBZg8cyRa3iw1XB9iGVFCnXgb81HpIaE8hjERq1vac1Y0m6OX5QManX6g-WG1A2bZg2Yaa0MLS0PdYj2oY2f8f942lZE2q9JtSKFydhLkptft7weANrmrS0s2WhrdCIFwKwqwa7bQcRWwiQ9wsZg8aYxdMjWyQjc08iCiijRzcxSiID0UVcpozXVBe69dZsmcjcrAfcGV7XvjHWf8gof8ujw137g8-Ws2K0q1Yb0Lw38AEBI3Yr4rs2c3D2i1j2A4M1U3z280M3Xdb2c2x1a2H2bxIw5Vi3jBS3YZy3XBK3A9nBWYuD-WAIEAVIDWTIeJBgcJChXJ1gNIOge3Ko+3qisip3R1tmZYM7Wnip1qg3VAwkyCPYck2UuDUzbV12ir-DGidNXWSB8ByNUBI15AlAoLxBOO0DwwMAKBP0AxYtf0LW+6KDehdgrB3NsWA9IoptWAsgFAzYiAhKJqzikpZ3JOF37E56aWBao3E4c9yKt7D7E7yA16UhXzwxm4MgeQox8py4pBWsIKulkQHcCYiwoqJ13Zy5A8uEy58wn124vQIsotUoIkkAipS4ILoxQKH6S3toWAks+gK8yOk3VBQQQ1pPyNCWVCSX7ZXoyXnKzhXLKXcar2Oh6XgqmWisoXWWjPLaW6uWkrCqi58O1pbcuFqDrGnXQDvQwbDRg9TJBgThPoMIsJRunXJh8ICRlglISI0g7QKJg8qIjgfJzhFvR1evKvqD2oNuxyThlTZuf9SxyxzRqwrRzuMUNxwiWxg82wu3OwDJDxjxTw5oqnx5rZbx7xEwnwXwZuJopoucU97ZimtHox5ktHULyz3XAaUA4QwOUg-480XvAEsYC3-3lOZFbMcOmjIZIsJBRMrAtUqrZRfvF4aAqqn1qEjBLlG8qVn5X535P5gARgSgz4oelLLlZAiFgBokhByQkBgAAYMSAB2AADgxLwml8l-ARLgAFpIxle01leQ3T3BBleNBlfBKZLdeS5+YUnUpK0u5ykqkxryQeKcyc9bgooLhtlZjfv5OfWaYMd8XMsVmDmAYyujaKuTbqu2XjOxg6vGWLnGurm8aWu0a2vEq7Db8edVCXDKt2d3CRXPCadyi1XYCNWsitWh37rdC9WQCDWJ2kjJXoCmdNEaubAW75T8Ayai8l0B5NBdPB5q0er85yNmywKmB8B52dbnbTHYsfpARAiC+oVtWunR2SjwDK-8Pq-39a+Q-Wv4rG+BMGBybP1nNB4O-NAu+rY9U92+-hMe7MCsFgdYhPASKbA8+uyahGPsUECWPOtIZ0CNHKmR+rYx-APbR58iWOQvRS3oz8HqpfeIgv3th2dvYm9PskXyTpDk5+Y5CvtAOdo545UxCPUEYFRYLkXe6gd3ujiJZe99mkpAAGz+8KW4dNfmjXD5joGuiJPmM1wJrstr2CVGwjyzFCOExWKfAVm4U5yZ8mc4rL-FX3gLMcJ+PQFYvgEMCcBVk4GPuKKAoAuNfweeAno-2yJBUP8OrAyhAPL5QD9A-MS4JcAAAFmg4dpGVL7z8jWBgnPg6xr6oA6+DfB-jUSX5iDcUb-LMjDEKasgKaGAHgEgBwHKdak9SBgDqiUyd4jwP6JskP2k7m9xU8nHeDPhU5KEgoklNTtgC8E4YUAPRPoljGOp5A1OLuEHK4FRiHIcceWM5ODjqbzpEYhQHOD2Ck7604cnAbfHJDOT8wAwGAGbPYnCCUp0A1KBKNIimzhwcUZZevFwjJgogt4LfRvFJysAj528hxLvPIAkxidVgEnUao7CwSLVFAcEQmE5mYBYCsIY6A4lDU0AJDHYB-FMJZhwG5Ivs5SXIVXAIIkCq8HBdbE3k+w4JDqE1exHk0bINwaA-IO0J+jOBL5zwtQrcDcQ8ALZwwYnSwE2T1CSJH6ShIrtjluy442hu+KPm8jEboN8K2pf0gaWvz05eWyfCrIKwz7c4hBXhFHKIO+INEJBuHAdrSSsFpEDBkrVXKhV07zsrWS7QOCbhbK9UFWfuMcgHjtwO5wwyJF3PLHdy6Ji6JJX3BOEdj7dn+bI9Vr-mjyjoQBd+M4RVXlgQ96RaebLjGGFTsA76VgGfJGgLwIxgR4qEvP9nXQV5yAhbGvCUmzzkM2ATeUNAsIZid5u8PePvIjkHxbBh8vRUfKL26j9VIYWNLnL0MKD9Dnano4at6LDGLCQc5DN1mgBkJAD5CMALlB72qgHB3EkpLwUzEKjhhyuYgVALu09b-AXgXKPpMWKJhPojACgVwB0GBqVjKuNYspNUHrGWVK43vOAOULxxOQsRjAkwrs3xHWx5YdOQEEnyEG8DH8VWSkaKy0ESt8OnovuhEDhgljK0ZYjOEsXVGatpcrIsohyKmhq554S8HCjyMXbv5DGAo-viJkVH+59uEo+ob3BRJGNwwHueUT7k0QijcwYolOKqNtTqitxcMHcfWXtgti2xlgGwFBiPFRjIAXg0MHkjhh4UT8fpTAgUy0Tr1ymVKZ8oszexLUykhCW0cVBEStF2iiIuHj7SvS+ImyxQUhDxxtFycUYUwJQgCBtSk8CMI4uSGOKZIx4RECpSCaGklJwT2xiE3ND6O3gAdzwp2JnGXBmwPZl2epWzDVHyRIoQQLUCQDpNzAJkyMp-ZqoXn9SkJOqWCe4V5l0BnAB236BfANhDGM468iZK9BEGoTcISg1HGHq3C-6KSckNGKhO+jSp+SxJBAL0S3lTHkhqGEgURvxPREVCxxccBMqJPtLiTUoe4o5OWP47RT5JTTS2ASNtimZZWEmGbGXDjiQAtJggAyXpKWTW5n8RPSAJ3AZo2jYoCpFifJnDB+piEFk0jnrQk4NUkgcgRQAF18a0YQpwxBMNvFEkZj2OtoGbB2SAGEC8gPE6ZvfgEnuphJVU38NpJzB6SDJ4QBMrK2aGtDRGPUjqlROEBntzhXAYaYlCSDroS6m6S9LFPimqFNpscT1DtLFB7TdJcUw6SIFIY0UUmcUlCRAF04vFYugkFEA3ESRIBMAgwdANFEHi9IDgVsKyTgjklPC8Wg4snrMObzhgwR9EovOKkHgQi2MmQFAFABSzsgQiG2NAIjJoDRRowJ5ZmnqHZwrSZiJA06RiNejjjjC+6SCagCykHixA-oZsa2OknA05x9QBcU4SXFup+BL+KFFnw3HB5xJ0E0WTlOQmMj8+zIsdnLn0EmtLxXIm8bBjvE9C+RgWP4ElyAKqBXxoo98eiSlErs-gsooFP+L+COzgJKop1sv30AQSIp245MZJKlkISkJjU1abFI2mJS8c-M4SfCXDAHF3RDeTMeQHap9SPWZSRuFNiWlTY6JNwXmRUNej4zxJ2sjOInTDnwSOxuaCIWTGEgsgJOFM00K2mqlYR9p+khqYgR0xrZv28YTXOazI4SdoZfjbgFlEE7iBxCh8IYo7AomogkArPN+B-CaCTRQQbACQMOEF5V5iQDNeKHAGACztgA-7VmsADwhOQ5IeEDEpMyqidx8AxFViLNQXbadVAfwqwCMHtLCZZQXRWOuQn-hLz2eq84AGcFkAsAOMMlZgOgBASyDgAYC5gMAAABKQgThHAAAD6EGfmGUjRmmNneJAwub93DiojhxccuSPzMhZE4UALQCueGGuDVzpZuaJtJwBgG9BeyWE74ldllbhw4xogUoGXB7FMK0cQAA">TypeScript playground</a>.)</em></p>

<p>Let’s say we have a database with articles and users. We want to write some function to get items from the database.</p>

<p>Let’s define what are articles an users first.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="kr">interface</span> <span class="nx">Article</span> <span class="p">{</span>
  <span class="nl">title</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span>
  <span class="nx">content</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span>
  <span class="nx">date</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span>
  <span class="nx">hidden</span><span class="p">:</span> <span class="nx">boolean</span>
  <span class="nx">lang</span><span class="p">:</span> <span class="dl">"</span><span class="s2">en</span><span class="dl">"</span> <span class="o">|</span> <span class="dl">"</span><span class="s2">fr</span><span class="dl">"</span> <span class="o">|</span> <span class="dl">"</span><span class="s2">es</span><span class="dl">"</span>
<span class="p">}</span>

<span class="kr">interface</span> <span class="nx">User</span> <span class="p">{</span>
  <span class="nl">login</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span>
  <span class="nx">hashedPassword</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span>
  <span class="nx">salt</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span>
  <span class="nx">description</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Let’s create some users and articles.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">users</span><span class="p">:</span> <span class="nx">User</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span>
  <span class="p">{</span> <span class="na">login</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Alice</span><span class="dl">"</span><span class="p">,</span> <span class="na">hashedPassword</span><span class="p">:</span> <span class="dl">"</span><span class="s2">a</span><span class="dl">"</span><span class="p">,</span> <span class="na">salt</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ah</span><span class="dl">"</span><span class="p">,</span> <span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Hi, I am Alice and this is my personal space.</span><span class="dl">"</span> <span class="p">},</span>
  <span class="p">{</span> <span class="na">login</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Bob</span><span class="dl">"</span><span class="p">,</span> <span class="na">hashedPassword</span><span class="p">:</span> <span class="dl">"</span><span class="s2">b</span><span class="dl">"</span><span class="p">,</span> <span class="na">salt</span><span class="p">:</span> <span class="dl">"</span><span class="s2">bh</span><span class="dl">"</span><span class="p">,</span> <span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Hello, I am Bob.</span><span class="dl">"</span> <span class="p">},</span>
  <span class="p">{</span> <span class="na">login</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Carol</span><span class="dl">"</span><span class="p">,</span> <span class="na">hashedPassword</span><span class="p">:</span> <span class="dl">"</span><span class="s2">c</span><span class="dl">"</span><span class="p">,</span> <span class="na">salt</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ch</span><span class="dl">"</span><span class="p">,</span> <span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Hi, I am not Alice.</span><span class="dl">"</span> <span class="p">},</span>
<span class="p">];</span>

<span class="kd">const</span> <span class="nx">articles</span><span class="p">:</span> <span class="nx">Article</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span>
  <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Hello</span><span class="dl">"</span><span class="p">,</span> <span class="na">content</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Goodbye.</span><span class="dl">"</span><span class="p">,</span> <span class="na">date</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">hidden</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="na">lang</span><span class="p">:</span> <span class="dl">"</span><span class="s2">en</span><span class="dl">"</span> <span class="p">},</span>
  <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Good morning</span><span class="dl">"</span><span class="p">,</span> <span class="na">content</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Happy afternoon.</span><span class="dl">"</span><span class="p">,</span> <span class="na">date</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="na">hidden</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="na">lang</span><span class="p">:</span> <span class="dl">"</span><span class="s2">en</span><span class="dl">"</span> <span class="p">},</span>
  <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Hola</span><span class="dl">"</span><span class="p">,</span> <span class="na">content</span><span class="p">:</span> <span class="dl">"</span><span class="s2">¿Que tal?</span><span class="dl">"</span><span class="p">,</span> <span class="na">date</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span> <span class="na">hidden</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="na">lang</span><span class="p">:</span> <span class="dl">"</span><span class="s2">es</span><span class="dl">"</span> <span class="p">},</span>
<span class="p">]</span>
</code></pre></div></div>

<p>And let’s mix all of that in an array that will act as our database.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">actualDb</span><span class="p">:</span> <span class="p">(</span><span class="nx">User</span> <span class="o">|</span> <span class="nx">Article</span><span class="p">)[]</span> <span class="o">=</span> <span class="p">[</span>
  <span class="nx">users</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="c1">// 0: Alice</span>
  <span class="nx">articles</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="c1">// 1: Hello</span>
  <span class="nx">users</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="c1">// 2: Bob</span>
  <span class="nx">articles</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="c1">// 3: Hola</span>
  <span class="nx">articles</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="c1">// 4: Good morning</span>
  <span class="nx">users</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="c1">// 5: Carol</span>
<span class="p">]</span>
</code></pre></div></div>

<p>Now we can write a function that gets an item from the database.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getFromDb1</span><span class="p">(</span><span class="nx">id</span><span class="p">:</span> <span class="kr">number</span><span class="p">)</span> <span class="cm">/* inferred return type is User | Article */</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">actualDb</span><span class="p">[</span><span class="nx">id</span><span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>

<p>But what if there is no item in the database with the id we passed as a parameter? <code class="language-plaintext highlighter-rouge">getFromDb1(42)</code> would return undefined but the return type of the function doesn’t say undefined. There is two way to go about this.</p>
<ol>
  <li>Change TypeScript configuration to enable <a href="https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess">noUncheckedIndexedAccess</a> which says to the compiler that every access to an array index could return <code class="language-plaintext highlighter-rouge">undefined</code>, it would make the return type of <code class="language-plaintext highlighter-rouge">getFromDb1</code> inferred as <code class="language-plaintext highlighter-rouge">User | Article | undefined</code>. Technically, it is true that every indexed access to an array can return undefined but it may be quite cumbersome if generalized to the whole codebase, for example, if you enable it you’ll see that the declaration of actualDb has an error at every line.</li>
  <li>Change the body of the function, see <code class="language-plaintext highlighter-rouge">getFromDb2</code> below</li>
</ol>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getFromDb2</span><span class="p">(</span><span class="nx">id</span><span class="p">:</span> <span class="kr">number</span><span class="p">)</span> <span class="cm">/* inferred return type is User | Article | null */</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">id</span> <span class="o">&gt;=</span> <span class="nx">actualDb</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span> <span class="c1">// we decide to return null instead of undefined here</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nx">actualDb</span><span class="p">[</span><span class="nx">id</span><span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This is much better in my opinion since it actually forces us to check if the element is actually in the database.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">item1</span><span class="p">:</span> <span class="nx">User</span> <span class="o">|</span> <span class="nx">Article</span> <span class="o">=</span> <span class="nx">getFromDb2</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// Good, the compiler give an error here "Type 'Article | User | null' is not assignable to type 'Article | User'. Type 'null' is not assignable to type 'Article | User'"</span>
<span class="kd">let</span> <span class="nx">item2</span><span class="p">:</span> <span class="nx">User</span> <span class="o">|</span> <span class="nx">Article</span> <span class="o">|</span> <span class="kc">null</span> <span class="o">=</span> <span class="nx">getFromDb2</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// This works, we explicitly says that item2 may be null</span>
<span class="kd">let</span> <span class="nx">item3</span> <span class="cm">/* inferred type is User | Article | null */</span> <span class="o">=</span> <span class="nx">getFromDb2</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// Obviously you don't need to explicitly write the type, TypeScript is able to perfectly infer it</span>
<span class="kd">let</span> <span class="nx">item4</span><span class="p">:</span> <span class="nx">User</span> <span class="o">|</span> <span class="nx">Article</span> <span class="o">=</span> <span class="nx">getFromDb1</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// no error with the previous version, which is kind of dangerous because it can (and probably will at some point) return `undefined`</span>
</code></pre></div></div>

<h2 id="get-one-item-of-a-specific-type">Get one item of a specific type</h2>

<p>Now let’s say that you want to make a function to get a User by its id. You could do something like</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getUserById1</span><span class="p">(</span><span class="nx">id</span><span class="p">:</span> <span class="kr">number</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">getFromDb2</span><span class="p">(</span><span class="nx">id</span><span class="p">);</span> <span class="c1">// we use our best version of getFromDb</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It would work as long as you’re passing an id that is from an actual User. But this function could also return a Article (the inferred return type of getUserById1 is <code class="language-plaintext highlighter-rouge">User | Article | null</code>), which is probably not what we want (it’s named “getUser” after all).</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getUserById2</span><span class="p">(</span><span class="nx">id</span><span class="p">:</span> <span class="kr">number</span><span class="p">)</span> <span class="cm">/* inferred return type is User | null */</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">item</span> <span class="o">=</span> <span class="nx">getFromDb2</span><span class="p">(</span><span class="nx">id</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">item</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="dl">"</span><span class="s2">login</span><span class="dl">"</span> <span class="k">in</span> <span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Here TypeScript knows that item is a User because it has a login field, and between User and Article only User has a login field</span>
    <span class="k">return</span> <span class="nx">item</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="kc">null</span><span class="p">;</span> <span class="c1">// when item is not a User we return null, not much we can do here, the caller probably made a mistake, we could throw an exception but... (see after about exceptions)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Let’s do the same thing for Article.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getArticleById2</span><span class="p">(</span><span class="nx">id</span><span class="p">:</span> <span class="kr">number</span><span class="p">)</span> <span class="cm">/* inferred return type is Article | null */</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">item</span> <span class="o">=</span> <span class="nx">getFromDb2</span><span class="p">(</span><span class="nx">id</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">item</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="dl">"</span><span class="s2">title</span><span class="dl">"</span> <span class="k">in</span> <span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">item</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Good. And now let’s say we want a function that can fetch any item from the database, check it it’s of the right type (User of Article) and actually return it only if it’s ok. Something like:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getAnyItem</span><span class="p">(</span><span class="kd">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">article</span><span class="dl">"</span> <span class="o">|</span> <span class="dl">"</span><span class="s2">user</span><span class="dl">"</span><span class="p">,</span> <span class="nx">id</span><span class="p">:</span> <span class="kr">number</span><span class="p">)</span> <span class="cm">/* inferred return type is User | Article | null */</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">item</span> <span class="o">=</span> <span class="nx">getFromDb2</span><span class="p">(</span><span class="nx">id</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">item</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="kd">type</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">article</span><span class="dl">"</span> <span class="o">&amp;&amp;</span> <span class="dl">"</span><span class="s2">title</span><span class="dl">"</span> <span class="k">in</span> <span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">item</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="kd">type</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">user</span><span class="dl">"</span> <span class="o">&amp;&amp;</span> <span class="dl">"</span><span class="s2">login</span><span class="dl">"</span> <span class="k">in</span> <span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">item</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="p">{</span>
    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It looks good. Except that it’s not very practical to use.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getIdFromUser</span><span class="p">()</span> <span class="p">{</span>
  <span class="c1">// we use a random generator here but the idea is that the user is typing the id by hand so it could be anything</span>
  <span class="k">return</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="mi">10</span><span class="p">);</span> <span class="c1">// a number between 0 and 9</span>
<span class="p">}</span>

<span class="kd">let</span> <span class="nx">a</span> <span class="cm">/* inferred type is User | Article | null */</span> <span class="o">=</span> <span class="nx">getAnyItem</span><span class="p">(</span><span class="dl">"</span><span class="s2">article</span><span class="dl">"</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> <span class="c1">// will return article Hello</span>
<span class="kd">let</span> <span class="nx">b</span> <span class="cm">/* inferred type is User | Article | null */</span> <span class="o">=</span> <span class="nx">getAnyItem</span><span class="p">(</span><span class="dl">"</span><span class="s2">user</span><span class="dl">"</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="c1">// will return user Alice</span>
<span class="kd">let</span> <span class="nx">c</span> <span class="cm">/* inferred type is User | Article | null */</span> <span class="o">=</span> <span class="nx">getAnyItem</span><span class="p">(</span><span class="dl">"</span><span class="s2">article</span><span class="dl">"</span><span class="p">,</span> <span class="nx">getIdFromUser</span><span class="p">());</span> <span class="c1">// will return one of the article or null</span>
<span class="kd">let</span> <span class="nx">d</span> <span class="cm">/* inferred type is User | Article | null */</span> <span class="o">=</span> <span class="nx">getAnyItem</span><span class="p">(</span><span class="dl">"</span><span class="s2">user</span><span class="dl">"</span><span class="p">,</span> <span class="nx">getIdFromUser</span><span class="p">());</span> <span class="c1">// will return one of the user or null</span>
</code></pre></div></div>

<p>We KNOW that <code class="language-plaintext highlighter-rouge">c</code> will be NOT be of type User. It can’t because if it’s something else the check <code class="language-plaintext highlighter-rouge">type == "user" &amp;&amp; "login" in item</code> will be false and the function will return null.</p>

<p>In the same way we KNOW that d CANNOT BE of type Article.</p>

<p>Never the less the types of <code class="language-plaintext highlighter-rouge">c</code> and <code class="language-plaintext highlighter-rouge">d</code> are <code class="language-plaintext highlighter-rouge">User | Article | null</code>. It would be nice if because of the first parameter being “article” we were able to tell TypeScript that the return type was going to be an Article. Same thing when the first parameter is “user”.</p>

<p>Enter mapped types!</p>

<h2 id="mapped-types">Mapped Types</h2>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">interface</span> <span class="nx">DbObjectMapping</span> <span class="p">{</span>
  <span class="dl">"</span><span class="s2">article</span><span class="dl">"</span><span class="p">:</span> <span class="nx">Article</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">user</span><span class="dl">"</span><span class="p">:</span> <span class="nx">User</span><span class="p">,</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nx">getAnyItem2</span><span class="o">&lt;</span><span class="nx">T</span> <span class="kd">extends</span> <span class="kr">keyof</span> <span class="nx">DbObjectMapping</span><span class="o">&gt;</span><span class="p">(</span><span class="kd">type</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">id</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">DbObjectMapping</span><span class="p">[</span><span class="nx">T</span><span class="p">]</span> <span class="o">|</span> <span class="kc">null</span> <span class="p">{</span>
  <span class="c1">// let's leave the body empty for now</span>
  <span class="k">return</span> <span class="kc">null</span><span class="o">!</span><span class="p">;</span> <span class="c1">// just a hack to remove the warning from the TypeScript compiler, ignore it</span>
<span class="p">}</span>
</code></pre></div></div>

<p>A few explanations are probably necessary. First what does <code class="language-plaintext highlighter-rouge">keyof DbObjectMapping</code> means?</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">e</span><span class="p">:</span> <span class="kr">keyof</span> <span class="nx">DbObjectMapping</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">article</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// ok</span>
<span class="nx">e</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">user</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// ok</span>
<span class="nx">e</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">hello</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// error: Type '"hello"' is not assignable to type 'keyof DbObjectMapping'.</span>
</code></pre></div></div>

<p>So <code class="language-plaintext highlighter-rouge">keyof DbObjectMapping</code> is basically <code class="language-plaintext highlighter-rouge">"article" | "user"</code>, the possible values that the <strong>keys of</strong> DbObjectMapping could have. The keys are the named to the left of the “:” in DbObjectMapping.</p>

<p>Second, what does <code class="language-plaintext highlighter-rouge">T extends keyof DbObjectMapping</code> means? It means that we declare a type variable called T that must be of type <code class="language-plaintext highlighter-rouge">keyof DbObjectMapping</code> so either “article” or “user”. The <code class="language-plaintext highlighter-rouge">type</code> parameter must be of this type so it can only be “article” or “user”.</p>

<p>Third, what about <code class="language-plaintext highlighter-rouge">DbObjectMapping[T]</code>? Well now that we know that T is <code class="language-plaintext highlighter-rouge">"article" | "user"</code> it quite obvious. <code class="language-plaintext highlighter-rouge">DbObjectMapping[T]</code> will be <code class="language-plaintext highlighter-rouge">User</code> when <code class="language-plaintext highlighter-rouge">T</code> is <code class="language-plaintext highlighter-rouge">"user"</code> and <code class="language-plaintext highlighter-rouge">Article</code> when <code class="language-plaintext highlighter-rouge">T</code> is <code class="language-plaintext highlighter-rouge">"article"</code>.</p>

<p>Let’s see how we can use that.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// reminder: [0: Alice, 1: Hello, 2: Bob, 3: Hola, 4: Good morning, 5: Carol]</span>
<span class="kd">let</span> <span class="nx">u1</span> <span class="cm">/* inferred type is User | null */</span> <span class="o">=</span> <span class="nx">getAnyItem2</span><span class="o">&lt;</span><span class="dl">"</span><span class="s2">user</span><span class="dl">"</span><span class="o">&gt;</span><span class="p">(</span><span class="dl">"</span><span class="s2">user</span><span class="dl">"</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="c1">// should return user Alice</span>
<span class="kd">let</span> <span class="nx">a1</span> <span class="cm">/* inferred type is Article | null */</span> <span class="o">=</span> <span class="nx">getAnyItem2</span><span class="o">&lt;</span><span class="dl">"</span><span class="s2">article</span><span class="dl">"</span><span class="o">&gt;</span><span class="p">(</span><span class="dl">"</span><span class="s2">article</span><span class="dl">"</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> <span class="c1">// should return article Hello</span>
<span class="kd">let</span> <span class="nx">a2</span> <span class="cm">/* inferred type is Article | null */</span> <span class="o">=</span> <span class="nx">getAnyItem2</span><span class="o">&lt;</span><span class="dl">"</span><span class="s2">article</span><span class="dl">"</span><span class="o">&gt;</span><span class="p">(</span><span class="dl">"</span><span class="s2">article</span><span class="dl">"</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span> <span class="c1">// should return null since the id with id 2 is a User and we are asking for an Article</span>
</code></pre></div></div>

<p>“But we are just writing “user” twice!? What’s the point?!” you may ask. And you are right. luckily for us, TypeScript is smart, you can just remove the first instance and it will deduce that since the parameter <code class="language-plaintext highlighter-rouge">type</code> is “user” (or “article”) then T must be “user” (or “article”).</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">u3</span> <span class="cm">/* inferred type is User | null */</span> <span class="o">=</span> <span class="nx">getAnyItem2</span><span class="p">(</span><span class="dl">"</span><span class="s2">user</span><span class="dl">"</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="c1">// the type is inferred as getAnyItem2&lt;"user"&gt;(type: "user", id: number): User | null</span>
<span class="kd">let</span> <span class="nx">a3</span> <span class="cm">/* inferred type is Article | null */</span> <span class="o">=</span> <span class="nx">getAnyItem2</span><span class="p">(</span><span class="dl">"</span><span class="s2">article</span><span class="dl">"</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> <span class="c1">// the type is inferred as getAnyItem2&lt;"article"&gt;(type: "article", id: number): Article | null</span>
</code></pre></div></div>

<p>Now let’s try to write the body of our function.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getAnyItem3</span><span class="o">&lt;</span><span class="nx">T</span> <span class="kd">extends</span> <span class="kr">keyof</span> <span class="nx">DbObjectMapping</span><span class="o">&gt;</span><span class="p">(</span><span class="kd">type</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">id</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">DbObjectMapping</span><span class="p">[</span><span class="nx">T</span><span class="p">]</span> <span class="o">|</span> <span class="kc">null</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">item</span> <span class="o">=</span> <span class="nx">getFromDb2</span><span class="p">(</span><span class="nx">id</span><span class="p">);</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">item</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// No item with this id has been found</span>
    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="kd">type</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">article</span><span class="dl">"</span> <span class="o">&amp;&amp;</span> <span class="dl">"</span><span class="s2">title</span><span class="dl">"</span> <span class="k">in</span> <span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// TypeScript knows that item is an Article here</span>
    <span class="k">return</span> <span class="nx">item</span><span class="p">;</span>
    <span class="cm">/*
    We have an error here:
    Type 'Article' is not assignable to type 'DbObjectMapping[T]'.
      Type 'Article' is not assignable to type 'Article &amp; User'.
        Type 'Article' is missing the following properties from type 'User': login, hashedPassword, salt, description
    */</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="kd">type</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">user</span><span class="dl">"</span> <span class="o">&amp;&amp;</span> <span class="dl">"</span><span class="s2">login</span><span class="dl">"</span> <span class="k">in</span> <span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// TypeScript knows that item is an User here</span>
    <span class="k">return</span> <span class="nx">item</span><span class="p">;</span>
    <span class="cm">/*
    Error:
    Type 'User' is not assignable to type 'DbObjectMapping[T]'.
      Type 'User' is not assignable to type 'Article &amp; User'.
        Type 'User' is missing the following properties from type 'Article': title, content, date, hidden, lang
    */</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="p">{</span>
    <span class="c1">// the type parameter does not match the actual type of the item</span>
    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We have errors here. Weird errors. And sadly I don’t know any clean way of fixing it.
What happens is that TypeScript knows that <code class="language-plaintext highlighter-rouge">DbObjectMapping[T]</code> can be an <code class="language-plaintext highlighter-rouge">Article</code> or a <code class="language-plaintext highlighter-rouge">User</code> and understands it as the fusion of the two, as an objects with the field of BOTH. You can be sure of this because the following code does not give any error.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getAnyItem4</span><span class="o">&lt;</span><span class="nx">T</span> <span class="kd">extends</span> <span class="kr">keyof</span> <span class="nx">DbObjectMapping</span><span class="o">&gt;</span><span class="p">(</span><span class="kd">type</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">id</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">DbObjectMapping</span><span class="p">[</span><span class="nx">T</span><span class="p">]</span> <span class="o">|</span> <span class="kc">null</span> <span class="p">{</span>
  <span class="k">return</span> <span class="p">{</span>
    <span class="c1">// field from Article</span>
    <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Hello</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">content</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Goodbye.</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">date</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
    <span class="na">hidden</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
    <span class="na">lang</span><span class="p">:</span> <span class="dl">"</span><span class="s2">en</span><span class="dl">"</span><span class="p">,</span>
    <span class="c1">// fields from User</span>
    <span class="na">login</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Alice</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">hashedPassword</span><span class="p">:</span> <span class="dl">"</span><span class="s2">a</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">salt</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ah</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">description</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Hi, I am Alice and this is my personal space.</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// if we remove this line there will be an error: Property 'description' is missing in type</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>You can check <a href="https://stackoverflow.com/questions/59789187/type-is-not-assignable-to-mapped-type">this Stack Overflow thread</a> for an other explanation.</p>

<p>We can “fix” the function this way:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getAnyItem5</span><span class="o">&lt;</span><span class="nx">T</span> <span class="kd">extends</span> <span class="kr">keyof</span> <span class="nx">DbObjectMapping</span><span class="o">&gt;</span><span class="p">(</span><span class="kd">type</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">id</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">DbObjectMapping</span><span class="p">[</span><span class="nx">T</span><span class="p">]</span> <span class="o">|</span> <span class="kc">null</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">item</span> <span class="o">=</span> <span class="nx">getFromDb2</span><span class="p">(</span><span class="nx">id</span><span class="p">);</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">item</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="kd">type</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">article</span><span class="dl">"</span> <span class="o">&amp;&amp;</span> <span class="dl">"</span><span class="s2">title</span><span class="dl">"</span> <span class="k">in</span> <span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">item</span> <span class="k">as</span> <span class="nx">DbObjectMapping</span><span class="p">[</span><span class="nx">T</span><span class="p">];</span> <span class="c1">// we basically say to TypeScript to ignore the error because we know what we are doing</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="kd">type</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">user</span><span class="dl">"</span> <span class="o">&amp;&amp;</span> <span class="dl">"</span><span class="s2">login</span><span class="dl">"</span> <span class="k">in</span> <span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">item</span> <span class="k">as</span> <span class="nx">DbObjectMapping</span><span class="p">[</span><span class="nx">T</span><span class="p">];</span> <span class="c1">// we basically say to TypeScript to ignore the error because we know what we are doing</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="p">{</span>
    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And since we are doing the same thing when <code class="language-plaintext highlighter-rouge">type == "article" &amp;&amp; "title" in item</code> and when <code class="language-plaintext highlighter-rouge">type == "user" &amp;&amp; "login" in item</code> we can simplify the function to:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getAnyItem6</span><span class="o">&lt;</span><span class="nx">T</span> <span class="kd">extends</span> <span class="kr">keyof</span> <span class="nx">DbObjectMapping</span><span class="o">&gt;</span><span class="p">(</span><span class="kd">type</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">id</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="nx">DbObjectMapping</span><span class="p">[</span><span class="nx">T</span><span class="p">]</span> <span class="o">|</span> <span class="kc">null</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">item</span> <span class="o">=</span> <span class="nx">getFromDb2</span><span class="p">(</span><span class="nx">id</span><span class="p">);</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">item</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="c1">// we merge the two conditions here</span>
  <span class="k">else</span> <span class="k">if</span> <span class="p">((</span><span class="kd">type</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">article</span><span class="dl">"</span> <span class="o">&amp;&amp;</span> <span class="dl">"</span><span class="s2">title</span><span class="dl">"</span> <span class="k">in</span> <span class="nx">item</span><span class="p">)</span> <span class="o">||</span><span class="err"> </span><span class="p">(</span><span class="kd">type</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">user</span><span class="dl">"</span> <span class="o">&amp;&amp;</span> <span class="dl">"</span><span class="s2">login</span><span class="dl">"</span> <span class="k">in</span> <span class="nx">item</span><span class="p">))</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">item</span> <span class="k">as</span> <span class="nx">DbObjectMapping</span><span class="p">[</span><span class="nx">T</span><span class="p">];</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="p">{</span>
    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Not really satisfying if you ask me but I don’t know any other way to do it.</p>

<h2 id="why-not-use-exceptions">Why Not Use Exceptions?</h2>

<p>When we wrote <code class="language-plaintext highlighter-rouge">getUserById2</code> (duplicated below as <code class="language-plaintext highlighter-rouge">getUserById22</code>) I said that instead of returning <code class="language-plaintext highlighter-rouge">null</code> we could theoratically throw an exception but… and I didn’t explain why. Well it’s simple. There is no way in TypeScript to specify which exception a function might throw, which means that you can’t statically enforce catching them and I don’t like that.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getUserById22</span><span class="p">(</span><span class="nx">id</span><span class="p">:</span> <span class="kr">number</span><span class="p">)</span> <span class="cm">/* inferred return type is User | null */</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">item</span> <span class="o">=</span> <span class="nx">getFromDb2</span><span class="p">(</span><span class="nx">id</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">item</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="dl">"</span><span class="s2">login</span><span class="dl">"</span> <span class="k">in</span> <span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Here TypeScript knows that item is a User because it has a login field, and between User and Article only User has a login field</span>
    <span class="k">return</span> <span class="nx">item</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="kc">null</span><span class="p">;</span> <span class="c1">// when item is not a User we return null, not much we can do here, the caller probably made a mistake, we could throw an exception but... (see after about exceptions)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>So if instead we threw an exception we would have something like;</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">ItemNotFound</span> <span class="kd">extends</span> <span class="nb">Error</span> <span class="p">{};</span>
<span class="kd">class</span> <span class="nx">ItemOfWrongType</span> <span class="kd">extends</span> <span class="nb">Error</span> <span class="p">{};</span>

<span class="kd">function</span> <span class="nx">getUserById23</span><span class="p">(</span><span class="nx">id</span><span class="p">:</span> <span class="kr">number</span><span class="p">)</span> <span class="cm">/* inferred return type is User */</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">item</span> <span class="o">=</span> <span class="nx">getFromDb2</span><span class="p">(</span><span class="nx">id</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">item</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nx">ItemNotFound</span><span class="p">();</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="dl">"</span><span class="s2">login</span><span class="dl">"</span> <span class="k">in</span> <span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Here TypeScript knows that item is a User because it has a login field, and between User and Article only User has a login field</span>
    <span class="k">return</span> <span class="nx">item</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">throw</span> <span class="k">new</span> <span class="nx">ItemOfWrongType</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Notice how the return type is now just <code class="language-plaintext highlighter-rouge">User</code> instead of <code class="language-plaintext highlighter-rouge">User | null</code>. For the caller it looks like the call can’t fail. We can write:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
<span class="kd">let</span> <span class="nx">u</span> <span class="o">=</span> <span class="nx">getUserById23</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> <span class="c1">// it will throw an ItemOfWrongType exception since the item of id 1 is an Article</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">u</span><span class="p">.</span><span class="nx">login</span><span class="p">);</span> <span class="c1">// no error from the compiler, which make sense if you think about it, we will never reach this line since the previous line will throw an exception</span>
<span class="kd">let</span> <span class="nx">u2</span> <span class="o">=</span> <span class="nx">getUserById23</span><span class="p">(</span><span class="mi">42</span><span class="p">);</span> <span class="c1">// it will throw an ItemNotFound exception since there is no item with and id of 42</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">u2</span><span class="p">.</span><span class="nx">login</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Those call will fail and the compiler doesn’t warn us. With the previous version it would have said something:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
<span class="kd">let</span> <span class="nx">u</span> <span class="o">=</span> <span class="nx">getUserById22</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">u</span><span class="p">.</span><span class="nx">login</span><span class="p">);</span> <span class="c1">// with getUserById22 the compiler is able to warn us than u may be null</span>
<span class="kd">let</span> <span class="nx">u2</span> <span class="o">=</span> <span class="nx">getUserById22</span><span class="p">(</span><span class="mi">42</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">u2</span><span class="p">.</span><span class="nx">login</span><span class="p">);</span> <span class="c1">// same for u2</span>
<span class="p">}</span>
</code></pre></div></div>

<p>TypeScript could support some syntax to declare which exception a function can throw and force the caller to catch it. Like so (this syntax is NOT valid):</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getUserById24</span><span class="p">(</span><span class="nx">id</span><span class="p">:</span> <span class="kr">number</span><span class="p">)</span> <span class="cm">/* throws ItemNotFound, ItemOfWrongType */</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">item</span> <span class="o">=</span> <span class="nx">getFromDb2</span><span class="p">(</span><span class="nx">id</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">item</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nx">ItemNotFound</span><span class="p">();</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="dl">"</span><span class="s2">login</span><span class="dl">"</span> <span class="k">in</span> <span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Here TypeScript knows that item is a User because it has a login field, and between User and Article only User has a login field</span>
    <span class="k">return</span> <span class="nx">item</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">throw</span> <span class="k">new</span> <span class="nx">ItemOfWrongType</span><span class="p">();</span>
<span class="p">}</span>

<span class="p">{</span>
<span class="kd">let</span> <span class="nx">u</span> <span class="o">=</span> <span class="nx">getUserById24</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> <span class="c1">// and here we could have a compiler error saying something like "getUserById24 can throw ItemNotFound or ItemOfWrongType but they aren't catched"</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">u</span><span class="p">.</span><span class="nx">login</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>But TypeScript doesn’t support that. And it’s <a href="https://github.com/microsoft/TypeScript/issues/13219">not in the cards</a>.</p>

<p>The good news is that you can still use <a href="https://en.wikipedia.org/wiki/Result_type">Either types</a> or declare the function like this <code class="language-plaintext highlighter-rouge">function getUserById24(id: number) : ItemNotFound | ItemOfWrongType | User</code> and check the return type with <code class="language-plaintext highlighter-rouge">instanceof Error</code>.</p>]]></content><author><name></name></author><category term="typescript" /><category term="programming" /><category term="en" /><summary type="html"><![CDATA[(You can also read the whole article on the TypeScript playground.)]]></summary></entry><entry xml:lang="en"><title type="html">When to Use any in TypeScript</title><link href="https://playest.net/blog/2021/11/12/When-to-Use-any-in-TypeScript.html" rel="alternate" type="text/html" title="When to Use any in TypeScript" /><published>2021-11-12T00:00:00+01:00</published><updated>2021-11-12T00:00:00+01:00</updated><id>https://playest.net/blog/2021/11/12/When%20to%20Use%20any%20in%20TypeScript</id><content type="html" xml:base="https://playest.net/blog/2021/11/12/When-to-Use-any-in-TypeScript.html"><![CDATA[<p><em>(You can also read the whole article on the <a href="https://www.typescriptlang.org/play?#code/PTAEHUAsFMDtQC4HtQFcDO1E1AQ1gJ6IEAO0A-ALABQNANtAqOggE4CWsA5gGq52poALmZtOXUAF5QAIlwAjAMYBlMdxkBueo1EduAQXT7CI-EWlylxghpk0aLPb36CAdIsi5W+hAAoAzACUGqAgoADu7HR0oKyMqKzwMsp21I7ihtbunt5+QSFhkdGx8YmyKQA02FjouAC2WAiQ4sycijVqXJmEoOzoeIoIqPx0RLi64g6dfALQrnAAJujg7E2+MlwywaFgRTFxQ2VsglNO3QTzsEsraxtbBbtR+6Xwx9BVTTX1jc3crbDtCYGIw9PoDIYjMZArj2ahhfALCJYTwANx+Xwa2BaADMkKxQNA0awiA0mkglqA8aBsagAQh2EhXp4mNAAB59BD9RmU-EIXAAa2g-Vw-XS3FcoAAKr8uP0PPguFhwjB4OEsHUBVhxnUOZrXNomLBUHV5NBWDNBCIjSazVJQP4tNRrabzS45sgAKKskiMuD0-i+ABM20KTxKhyS-lcAAZo9AANTRmSgU2KXAYRpIL0+2B+9j8UBcdho-qfAne32wf0xWBIPn07lIbHVUDOs1VJDyFEMjCjLH9DWC0VwTCU2B93H4tusU7iC3urMV3NV-N0IMhsDoFCrUCDoXMEc-XBMJpgsHjRRIOokKK2s2sKnoNqZ7OV6u9foAOQA8pK8NDd0YSByVnYEsk9Jc8wDYMHhTVAT2afowVrJgLyvG8GHxe88SqVN01HMxAPwfoZAASVAfla3CCJmVAUiAHI6lABYkHEZNfAFcYACllF3cloECRAUDLS9r1vVgqh3PZRDDbFcCiPAmFYWl6UxMlUC4SBYTCZQr0YdgGn6NU0AInpInUw063YRQWl8OhOAQM1hTiEzoBpGJJ37QIDTwWDxjPa97JshA+zMGhxmkB0djgphJV4sF0A1VgmC3akHyYqix3-aTTWIMhERFVtjRdcLXAgnMoLXGDotrAlWAfCS8CQqtFmgBZSvlXJ11ghBSCFRQOBIJgFkYaBBlLHBsJnWhqAi2QYGiJBNGi0ryrfVduui3qyHQAb2CG5jRvGlspo6nIfE2sJatOmawk4ftQDTJCmHCJBUDoRFUSwU04BTRhHN5FARsUOgvC1JqiptfEAB9oSqezBX7HzcFwK1ittWGxQkSLYMlPrdsGpgwWQFBEq8JhPIwUAYBchFeiYeQ4gFCasDZeoSAYOiInwRzEWQXc9QgLBgdB2nxkKgADadQExzoJZi+nDscwZ9yaY8kTwbFsTGonUMhl1mG3InhX1s19Vm3AysXCqV2gjdsGPeijMgIhTyMvF+XQcLLc6i7qrCeR4IelihVgeiEG9q3VCcXx7bpt2Ig9-o8IzITo5aNkOWTutICy6X9E-AARaFtM3FAGAQJ2ShYykg7ZwLvqD8zc-GGk6QZWAaDbwYO9N1hv1YdPuFjkQsZlgAC6WAG8aFAXpsV8ABZY9IFcVgESvWPQAAPlAaNXAAVkEmfqDnueDgSJJcE0WfQAAX1v6A6FHE+z-DS-7UdOeH+oH+aDCAgb1Hr4EQMSISJQ6hIDRC2C+Rw+qyFHp0GWfdkx00AageixQULWSPLFeKsBtb1TajAl4xDtqZlABLOQyZYb+HlsqayudibKUbieJApMyA2WxNZakVI3pYVZOzTmqVyIkCDmWWBrx4F1AIJgOgzY6ZPgBFgCWY9YbTnlmCUSJBjzsHkJzZulDqHILoYrV6rBPYSgAArwXpH8AAVhgJgMhpyoJZu-OBZBE7vU+rgaBP14BoTEphOqDUaK-WMmwV2KAJaSNkNfCW-8wBmHCLgAgVRjKZS+v+buDYmTq0kaKPSql9zjGloo4pBlSlwzwFcDW2TaqpOiRRTK91cALBRPgQEDCPCSWNqAMgrBtaDF4fifh5YhHQAAIRJIevXDmWBzygHsggEKWBLxVg4GiBYuEg7oOAfAAyuAiy5gdkwfZiolJjTxBSbE6VamgH0FY8i8ccBPPIluUkVT+hxLKejfEFSvmGX-FjWEFc8AoztNOAeQ8uCxx6vApsEL-IkWlrLJwMgqhcHYQsGZFtLZrWXNWS6YBtHiVCXib2Ptzp+H9qS9C5KboowJUgWF8LormM9qXemVdxgrLWUidgrA+a0XkDnISr4iWrlqYiX2TAiwln-GSkJU0FZKO6cLRk4cWlIGouwZsqxeV9x5CCpBxk0xh1QoMYY0QiBpmKIRJFZJRykmAhSFSCljJg2YAkLATqcDkIlAAYRGC0ZAsKPzUk4DUZ8itM4sC5PAMVTQ8pakIE082+rfDkKbBxFGglJAWCxlsUAr9kXZC8H7e290E6My6bnchBN9pMEorqia6tmWRvGGPFOo5-WNCFBHU+5bCWVRJTKxW-BUqtvCO23WVcfx-j+VDQ2D0lk3R-k-F+t9mVW0lWOulvQmRgjrQCXOcUdWzrOciyNi7TVOAyc0DwRFYBzp5SbaW6r0QuTBIyPsgYBlIHQE+eQURVjsCFDQP+cIwCkWbJ8FyaoXL+EA8BvRnM4mNq7rSHu3JoWD06APAAQuwhg+AR4AWhpPf5yCxVIDI-AMtGyWCxDptIZeTQ14bzqPC2+Wb151IADx71cEEUtt9z4vHiTfYdP855bsWQvATiJhP7wAGzHwkx414LCv730fs-LAZbJMRk-rfH+0HwXMrmvh2FxHSPQHI8EGgWac0L2swWotnQS1lusxWrqh6a2IRTAJ59ja9oHRnW+6zXaAK9r9fBgNg6d27tHbbKqcc6k7inSgKL17DVfl-P+T9wlgvrvqpS3+NAFPieHfCOpNMtSdrBNAVYNN-x0YY6Ab8AAlYr-zb5WbtNZ2CHgxr8hbOQykCiUxEGAkSFoUDbSrD4wvNzeb81SAsJ1xzsAfNabCCJBlIS8tq1Qs1k2O2QGNa0yN8zBnt3Drnnd2TUHuWdm7G9dAfY2klALPMgxSo3ofWpn4rAjiWMsVzC5tbfVc0ea27IYtmnh1+bleOoLJ7QsNvxhFltVFosXfvS0eL03JvJdR6l62607Z+Sy6hZ+uWCf5croVpdxrUoJ3Kw1N71Aav6tANmuH7nmWedkFdvbKO56HZwMq20p3aIxaWRL6mZpoApZRo6TdhnavS7AEd4J8vmdneRSi-rK6buU817z2Z4AajATVCK9WBW-qrNtPzVOCw+iOPbtAxtguZBe-QD73De2X0kTxKgrLPzoAkDiJgFqiJ7oXvkK7WXORcAq3xDIaGyYEaLPgFLGj6KWiwwl-Qp9ucGjEVkNLR8SCqQS62Nyu3hY6z-n5ZzJs8H16KAm41jJSoeZkOEvwCbCg3oITTa7eBqUK5V2xSmTPE3+YrZg6AReQGmD9sQN88J8ByKp0Ii70nh+CLLL0evMBJuMzPQlAAVXQDavsZZ7J1rAZeHxoPoF+NYiNREGALQO4qeAsiMpobuvIpA4gyc8ErgcBGs3qLAqAfeEQbW9M8MjAVcmAyIuq4Clytc2+iWYIcenYDATE-Mv+7AIqKiWM5we6kE6W648s3CIol43KAAMpgaKGkvUmDh3nosAtEMQrpA0OwQIYYtqEBOSIWIwCIUBC0D0rnL8oEoQNhu3NyJcnIaeNwN+B9BRoRGWnErZoRqwCRvRrtuyjLlgG-lfmMPBMBKwHKMDr4tAmIs4mipRimA5vgG4iQqZlNgPgrDuJ-iDjlt-t9NAL9HUMgVXniOssdmmLzH0lXCQfIAoH2NEc+pAi5NokFMeMQq8s7s7GntAEQBmAVKoVVjNMxkwHIWIfIHaGWpocUjKCIM0a6uILoe1FUT5J8vIdwJ+NANRNIHUXoq4O0f0XCvbLVE-qwHHn0MiGrobB0QMUMZGk6vAmFOvtKFgKkWQagSmoRJ0rMEhG+hemCDwB6D1gAJoDJmg6hobQKGKfBMSKHh7Xr7IWrMRIC9EtHiAkYVaO5owrojF6T1HjGyF-HDzTEoCqqNYSiETV6vqyDkQzo0TqwvJMQsRsSroXpP4kA+ijgm77JonKjqz7LerYnihSjxRGQPh-CBH7IagjT-gPF8hgGwFwHco7G7i6iIwhGIi5TZE1CoDyBrJqGh54ALALDShxDQC+CwDAkuhS7aatigDxhmY9GzTSmymRG+CjHyAQkIBaEyixz2zSRxJyD+DsTGIalBAdj4gAAsgubw6p9oHwLCkaAAjEej6nUOgFUFDtqvgZiCwOTG1HAa4IEA6aAGpoLihnadGTRIwpGqkekUQChOiS9EPlWIPt4iDtkvnknvBrjoTMwPJIiH0doRIPnrINKGCLkr3GyJwpyObgbI1grBcnwYRG8RqEQFSH2a2O3rlMui6JigrCbq9F-hsmwO0tZFPiiZelmVzPUN8TiRqJst2kbKABDq2WGUQE+AwFWNyvzLlLUNrKAPslOQWXwfzCxDDkLmQLmlWTKIMeEGLi4v8vtqjjqZAHKb4C+eIG+dWsesnNjssZMW+esSWV4iVmYmGPwE0v0DXGSazn9luLALAOkVgBxNKS0NLOAWqL9C8ROneu0l7n8OUnUt2g3gCsanTOMBLt5FqWEERkHHjDtHjonJauWWMIQNWfPJeUApOPgfzGNigWWFNqToibtiRKiZlKhSuViaxOoI+imcZFWVUngHEJGQrJmYhWkl7LdGAOwcgfyEQJTH2gkOWZ9viGCB2XTDuGCLSDOrAHihKXkjIcaVCVwERrMBRi5VRIxrfEYf8jCiYWYQxrxixSZVwdXCgOMm-jQDUaAHIURv9GaPUY0bfBMdWW0ZCSsb5bMDbtQOCoBaseEABqCQ0OlRAeCblaafbOVVwFBb+jBVgIFbqrALsi9LRCHM9C+lQKVToM1QCQ+I7oGEqbaNVdALVQDPVQVZMZYWADuEif0J8fgNqjXGdlUHLlhBVqwHiuRbqfKWlRlawAtd5YVWaXTk7uckAhaltaVurK1ghrtcdneAdXgFwPJG5dyvoNapCEJagAck9bUgQAJRIfAJ1eEG5VKMyFXGWKaOmL1GTqsBKKRClJEf6RRJwIiEihmAwMBrEOwJpAgOQKANcW9PRC5BwGTQ-iuDEPsqnGWHIOgMmEgIMseHiObL8YVWNbqm1P4FNfiDNXNZlWMQ1eIFvIVNOLBLVHtRSviIHP0juEzLasucZKklWBTa3miSblLX8AKXgIzqqYxd4fAFSDRU4JGeFL+f+WdXVZLYtdWTLf0NODCUrarnED1RrMbSOXhX8CrnTHBegMBD4jxQhhEQ8lNENWEHbtEFUGzcmCeiKNZEDSAjnLaE0uArUEQGREuYpQxMpWxFUJnhCBrS7uMDnfzHnYuZAiwH2KSbRMXWueoILihOiLIA3SFAQCWqTlHZGn0Y9DAH3shb6IPpgjEKJaVkBtYfkdNLMjyditIZDWgTDd1ROsqEQCfudQ7CoekpGpOesptRTHiPgRPkHKsHmdkvzM2UFKsH2DIE9AgOxJmeraFMAiwJPcUNgXgGKuIrRKDADIJGvkvSgJQQAUohIMnQSESBDVpWflgI7fNc7VdUtYJORF8a9e1uEOvCQIrA5BA9SDhp5UijIpSLDSanENtQGvjGTq-pfl4EQL4G8anU+Pan2E+DqKLOAng7gASUASlLGuRLua5JNvQzvvw4I38I2dyOrGWCLF4LotyE+I5MxR5b3IbVwOAPg-lFvIYVJigxLYado27camXhbcgljD1EDG1MgT8MTPAuRJ4HzOXOwuPguZeCyVSI5Cxqte9PSAsrvoZCVWEJ+LgVgyAqnNo7owI-lAceHUwIAX8IRC-egObDpEgB8KTMaGgCQFaISLaEfoQA-qOC5V1UeiwI5gsLzevh6GiEyB0AiF4IiDYcw6AarMFmyZqEZGgTxN+J+K4Doo4fKcxeCvYnaAM0MyM5gOsFPHfPcNFJM+eD0B2b2bJVzF8fedQPYq4HQPRtFXswtAc8tYk5PkrVUB2WWADjGq0A3ASLWBpC3HUsZGiVJLJPJDEAUh6piCrYJdM8M14HM4JJIsQhvX7c4UOcNKVn0HU3I-AFxBhVYsC9AHEwSW1L4PYhhbCogk4CqXEoC7M-Kdi4yLCoJIVBvVrj5PYpM9IEi4yCi6M+i-ozIAs0s2ECs8KGs0sRszXlE-ADs7S-s4c-bDdMKyc0gGczdFi5M7Lf8oEPQTbMSoepyv0J5AbhhLaP89JB9j2N9hZV84pLEL8+rsZRDOJVyuvhE9RPPqWGPv-Rc8nRKFTSDWmfokQPrbRBxdAMoFxVoh9YiL4L4wJugFqwsIJPzFxH4rgH64TC60Au603ZlCbj3WTopXuP0D63G82kPWIBiwVDnQsAkGGsFrOa+hhCo-ANUwQ3y8iTtca8pCuA0FUNm1xYXN+B6LxHevGpyBKDybA5zWaNzfiP1Txb281KANG50jm0NMkYmp-W22WdgoyAmyDV8QttiDvSyGnaMObLKxDB7UqzThlrjH1LO0wN42a8K2liq-bOW2G7eIiNO7G1xVewwzgHHoSPqxfrmEnSKCne7bqtcygGeZEZ3NsQjcUVzLUFQderA2CLWXnWGqVlgEu7m4rQXT3cm7gUXYxG3ZsI+iSFvqMBTTsT+kUpiJa+PbmHU1NgABqNGgCsh4viAhA-xTa3HSBTyXki3sc+QsegCMfcfMciDGJ3yOjgrmDMcQzXGwTGTZIgJwlLEyBBqMhEhPiNjtWgD0T0f0TgJTb0TXH6eDkjm8lhlgFjT4RYC5htbu6z6oBazWQQZVh9hLasCgwkD9AADakZAAuuOWkRw0DehwdDuI5NEKW+rIx3TLcQ0jnEAfAKJJAtW9uaZ+DD04KD5AQNJ6yBDBC4VHJ9FP8wnP1dC4WMWODHLpc-Bb-fSMUAquDE2yUl7cqL9PslEuApnu0MTTl64NSpWgzEHOrGRixvsrfSgB-UQNrbrO5dQFNgAFpMeCc2OgAABeIgEu-Hw1TAa3doeXhUC3sE-zHZLuSAE2pOMXdSS32Sw93CT8FI90yXjIfSqBdXnzCkPzzbPwb0mk3KbFqEP1nAHwZWJEAHY4fYE7xr1XLXdbb6AuTJmo2U9JEgL9tXHkRrX3JSsyQao9-IUX9167IC55T8dq6nZoesU2RDtSWddlJsuR6dvMAyD4+i0ATEQWNQl4XiSKCwx44wxxVBVbJqnAbhoAAvvPnlbxLjYOVY5YHMcRiIcAv3Dao+dA4+ADCAQAA">TypeScript playground</a>.)</em></p>

<p>Let’s start with a simple example.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">stringValue</span><span class="p">:</span> <span class="kr">string</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">abcString</span><span class="dl">"</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">stringAsAny</span><span class="p">:</span> <span class="kr">any</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">abcAny;</span><span class="dl">"</span>

<span class="nx">stringValue</span><span class="p">.</span><span class="nx">charAt</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span> <span class="c1">// will return "S"</span>
<span class="nx">stringAsAny</span><span class="p">.</span><span class="nx">charAt</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span> <span class="c1">// will return "S", the same thing since stringAsAny is actually a string</span>
<span class="nx">stringValue</span><span class="p">.</span><span class="nx">endsWith</span><span class="p">(</span><span class="dl">"</span><span class="s2">g</span><span class="dl">"</span><span class="p">);</span> <span class="c1">// will return true</span>
<span class="nx">stringAsAny</span><span class="p">.</span><span class="nx">endsWith</span><span class="p">(</span><span class="dl">"</span><span class="s2">g</span><span class="dl">"</span><span class="p">);</span> <span class="c1">// will return true, the same thing since stringAsAny is actually a string</span>
</code></pre></div></div>
<p>And we have the same thing for every methods or function that exists on or takes as string. Things change when we make a mistake.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">numberValue</span><span class="p">:</span> <span class="kr">number</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
<span class="nx">numberValue</span><span class="p">.</span><span class="nx">toExponential</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// will return "3.00e+0" because toExponential gives the exponential notation of the number, obviously this makes sense only for number</span>
<span class="nx">stringValue</span><span class="p">.</span><span class="nx">toExponential</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// so it makes sense that this is a compiler error since toExponential is NOT a string method</span>
<span class="nx">stringAsAny</span><span class="p">.</span><span class="nx">toExponential</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// but this is not a compiler error, because any means "I know what I'm doing" (aka JS mode) to the compiler, it will still fail at runtime though</span>
</code></pre></div></div>

<p>Sometimes we use any without noticing (linters are useful for this).</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">a</span><span class="p">;</span> <span class="c1">// a is implicitly any</span>
<span class="nx">a</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// but TypeScript is smart so from now on a will be typed as number</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">toExponential</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// no error, as intended</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">charAt</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// typescript detects the error</span>

<span class="nx">a</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">hello</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// </span>
<span class="nx">a</span><span class="p">.</span><span class="nx">toExponential</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// typescript detects the error</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">charAt</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// no error</span>
</code></pre></div></div>

<p>In this cas it would have been better to declare a as <code class="language-plaintext highlighter-rouge">number | string</code>, like this:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">aa</span><span class="p">:</span> <span class="kr">number</span> <span class="o">|</span> <span class="kr">string</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// TypeScript is too smart for us here and it breaks the example I wanted to make. We declare aa as `number | string` but it detects that we affect it a number so it is a number.</span>
<span class="nx">aa</span><span class="p">.</span><span class="nx">toExponential</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// that's why this works</span>
<span class="nx">aa</span><span class="p">.</span><span class="nx">charAt</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// but this doesn't</span>
<span class="nx">aa</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span> <span class="c1">// and this would work in any case since toString exists both on number AND string</span>
</code></pre></div></div>

<h2 id="return-a-disjunctive-type">Return a disjunctive type</h2>

<p>So, let’s redo out example but with a function:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">numberOrString</span><span class="p">():</span> <span class="kr">string</span> <span class="o">|</span><span class="err"> </span><span class="kr">number</span> <span class="p">{</span>
  <span class="k">if</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mf">0.5</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="dl">"</span><span class="s2">a</span><span class="dl">"</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="p">{</span>
    <span class="k">return</span> <span class="mi">3</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>You can try to remove the return type <code class="language-plaintext highlighter-rouge">string | number</code> and you’ll notice that TypeScript inferred the returned type to <code class="language-plaintext highlighter-rouge">"a" | 3</code> which is true but too specific for our example so I put the return type myself and since <code class="language-plaintext highlighter-rouge">string | number</code> is compatible with <code class="language-plaintext highlighter-rouge">"a" | 3</code> it works. Putting just “number” as the return type would have been a compiler error when we try to <code class="language-plaintext highlighter-rouge">return "a"</code>.</p>

<p>Anyway, we now have a function that returns sometimes a number and sometimes a string, and we have no way to know in advance which, it is perfect for our example!</p>

<p>This example is a little contrived, but you can imagine that you get records from an API and the API sometimes return a number and sometimes a string.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">aaa</span> <span class="o">=</span> <span class="nx">numberOrString</span><span class="p">();</span> <span class="c1">// type of aaa is "number | string", good!</span>
<span class="nx">aaa</span><span class="p">.</span><span class="nx">toExponential</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// compiler error</span>
<span class="nx">aaa</span><span class="p">.</span><span class="nx">charAt</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// compiler error</span>
<span class="nx">aaa</span><span class="p">.</span><span class="nx">toString</span><span class="p">();</span> <span class="c1">// works</span>
</code></pre></div></div>

<p>It’s a little weird that both <code class="language-plaintext highlighter-rouge">toExponential</code> and <code class="language-plaintext highlighter-rouge">charAt</code> gives a compiler error but since we don’t know if it’s a number or a string we can’t actually call any of those methods until we are sure of the type. Calling toString is fine since it exists on both type anyway.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="nx">aaa</span><span class="p">)</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">string</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">aaa</span><span class="p">.</span><span class="nx">charAt</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// in this branch typescript knows that aaa is a string because of the test</span>
  <span class="nx">aaa</span><span class="p">.</span><span class="nx">toExponential</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// and it also knows that it's NOT a number so this is a error</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
  <span class="nx">aaa</span><span class="p">.</span><span class="nx">toExponential</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// in this branch TypeScript knows that aaa is NOT a string, which means that it's a number since there is only 2 possibilities</span>
<span class="p">}</span>
</code></pre></div></div>

<p>If there were 3 possible return types:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">numberOrStringOrBoolean</span><span class="p">():</span> <span class="kr">string</span> <span class="o">|</span><span class="err"> </span><span class="kr">number</span> <span class="o">|</span> <span class="nx">boolean</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">rand</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">();</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">rand</span> <span class="o">&lt;</span> <span class="mf">0.3</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="dl">"</span><span class="s2">a</span><span class="dl">"</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="nx">rand</span> <span class="o">&lt;</span> <span class="mf">0.6</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="p">{</span>
    <span class="k">return</span> <span class="mi">3</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kd">let</span> <span class="nx">aaaa</span> <span class="o">=</span> <span class="nx">numberOrStringOrBoolean</span><span class="p">();</span>
<span class="k">if</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="nx">aaaa</span><span class="p">)</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">string</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">aaaa</span><span class="p">.</span><span class="nx">charAt</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// in this branch typescript knows that aaaa is a string because of the test</span>
  <span class="nx">aaaa</span><span class="p">.</span><span class="nx">toExponential</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// and it also knows that it's NOT a number so this is a error</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
  <span class="c1">// and here aaaa is either a boolean OR a number</span>
  <span class="kd">let</span> <span class="nx">a</span> <span class="o">=</span> <span class="nx">aaaa</span><span class="p">;</span> <span class="c1">// check the type of a by hovering over it</span>
  <span class="k">if</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="nx">aaaa</span><span class="p">)</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">boolean</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// the compiler knows that aaaa is a boolean here</span>
    <span class="nx">aaaa</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="p">{</span>
    <span class="nx">aaaa</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// obviously in a real example we would have just done</span>
<span class="k">if</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="nx">aaaa</span><span class="p">)</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">string</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">aaaa</span><span class="p">.</span><span class="nx">charAt</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// in this branch typescript knows that aaaa is a string because of the test</span>
  <span class="nx">aaaa</span><span class="p">.</span><span class="nx">toExponential</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// and it also knows that it's NOT a number so this is a error</span>
<span class="p">}</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="nx">aaaa</span><span class="p">)</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">boolean</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// the compiler knows that aaaa is a boolean here</span>
  <span class="nx">aaaa</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
  <span class="c1">// the compiler knows that aaaa is a number here</span>
  <span class="nx">aaaa</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We showed that it’s better to use disjunctive types (“disjunction” means “or” and is represented in TypeScript by the character “|” like in <code class="language-plaintext highlighter-rouge">number | string | boolean</code> which means “number or string or boolean”)</p>

<p>We got a little off track here, we wanted to talk about the any type so let’s go back to it.</p>

<h2 id="using-a-library">Using a Library</h2>

<p>Most of the times when I am forced to use any it’s because I use a library that uses it. Usually the library could have avoided using it by make better typings but… we are stuck with it, let’s see how to get out of this problem to avoid the <code class="language-plaintext highlighter-rouge">stringAsAny.toExponential(2)</code> fiasco.</p>

<p>Let’s say we have a lib called SomeLib with a method getSomething which return an any</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getSomethingOld</span><span class="p">():</span> <span class="kr">any</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">numberOrStringOrBoolean</span><span class="p">();</span> <span class="c1">// the library authors could have put "number | string | boolean" as the return type here, but it could also have been much more compilcated, it's probably much more complicated and that is why they used any</span>
<span class="p">}</span>

<span class="kd">const</span> <span class="nx">SomeLib</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">getSomething</span><span class="p">:</span> <span class="nx">getSomethingOld</span>
<span class="p">}</span>

<span class="kd">let</span> <span class="nx">somethingNew</span> <span class="o">=</span> <span class="nx">SomeLib</span><span class="p">.</span><span class="nx">getSomething</span><span class="p">();</span> <span class="c1">// no surprise here somethingNew is of type any</span>
</code></pre></div></div>

<p>The problem with any values is that TypeScript is VERY permissive with them which means that you can do.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">somethingBorrowed</span><span class="p">:</span> <span class="kr">number</span> <span class="o">=</span> <span class="nx">SomeLib</span><span class="p">.</span><span class="nx">getSomething</span><span class="p">();</span> <span class="c1">// no error here. any means "I know what I am doing" so TypeScript suppose that you know what you are doing. TypeScript is wrong here, you made a mistake but you told it you knew what you were doing, what's it supposed to do?</span>

<span class="c1">// The mistake could be more subtle</span>
<span class="kd">function</span> <span class="nx">addThree</span><span class="p">(</span><span class="nx">n</span><span class="p">:</span> <span class="kr">number</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">n</span> <span class="o">+</span> <span class="mi">3</span><span class="p">;</span>
<span class="p">}</span>

<span class="nx">addThree</span><span class="p">(</span><span class="nx">SomeLib</span><span class="p">.</span><span class="nx">getSomething</span><span class="p">());</span> <span class="c1">// will return "a3" ("a" + 3), or 4 (true + 3, true is 1 in sums, don't get me started...), or 6 (3 + 3), which is probably not what we want, we would have liked if typescript said something like "This function expects a number here but you gave any which may or may not be a number", but that would contradict the "I know what I am doing" mantra so it justs stay silent</span>

<span class="c1">// to be safe you would have to do</span>
<span class="k">if</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="nx">somethingNew</span><span class="p">)</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">number</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">addThree</span><span class="p">(</span><span class="nx">somethingNew</span><span class="p">);</span> <span class="c1">// in this branch somethingNew is of type number so it will always do what's reasonnable (adding number between them and NOT adding number and a string or a number and a boolean)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>But TypeScript won’t say anything if you forget to check the type because any means “I know what I am doing”, which we sometime are… but not always</p>

<p>Lucky for use our savior is here and it is unknown!</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getSomethingBlue</span><span class="p">():</span> <span class="nx">unknown</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">numberOrStringOrBoolean</span><span class="p">();</span>
<span class="p">}</span>

<span class="c1">// Let's redo our lib</span>
<span class="kd">const</span> <span class="nx">SomeBetterLib</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">getSomething</span><span class="p">:</span> <span class="nx">getSomethingBlue</span>
<span class="p">}</span>

<span class="kd">let</span> <span class="nx">somethingNew2</span> <span class="o">=</span> <span class="nx">SomeBetterLib</span><span class="p">.</span><span class="nx">getSomething</span><span class="p">();</span> <span class="c1">// somethingNew is of type unknown, but what does it mean?</span>
<span class="kd">let</span> <span class="nx">somethingBorrowed2</span><span class="p">:</span> <span class="kr">number</span> <span class="o">=</span> <span class="nx">SomeBetterLib</span><span class="p">.</span><span class="nx">getSomething</span><span class="p">();</span> <span class="c1">// it means you can't do that, compiler error!</span>
<span class="nx">addThree</span><span class="p">(</span><span class="nx">SomeBetterLib</span><span class="p">.</span><span class="nx">getSomething</span><span class="p">());</span> <span class="c1">// and that you can't do that either, compiler error again!</span>
</code></pre></div></div>

<p>Actually you can’t do anything with an unknown! That’s the beauty of it. It seems kind of useless right? You’re right. Until you use the “as” operator.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">somethingBorrowed3</span><span class="p">:</span> <span class="kr">number</span> <span class="o">=</span> <span class="nx">SomeBetterLib</span><span class="p">.</span><span class="nx">getSomething</span><span class="p">()</span> <span class="k">as</span> <span class="kr">number</span><span class="p">;</span> <span class="c1">// no compiler error but is it really what we want? We know that getSomething could also return a boolean or a string...</span>
<span class="nx">addThree</span><span class="p">(</span><span class="nx">SomeBetterLib</span><span class="p">.</span><span class="nx">getSomething</span><span class="p">()</span> <span class="k">as</span> <span class="kr">number</span><span class="p">);</span> <span class="c1">// no error here, but we could be adding boolean and number so shouldn't there be an error?</span>
</code></pre></div></div>

<p>Well, “as” is basically an other way to say “I know what I’m doing”, actually it’s a way to say “I mostly know what I’m doing” (note the “mostly”) because there is some checks done, we’ll get to those later.</p>

<p>The good thing with unknown, and why it’s better than any, is that we can’t forget about it, we have to explicitly “cast” (not really a cast, it’s a “type assertion”, we’ll see about that later) it.</p>

<p>To avoid using “as” everytime I use SomeBetterLib.getSomething() I can either wrap it into a function of my own or redo the types of the library (which is basically similar to wrapping it since I just use the types of the wrapping function at the declaration site).</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getSomethingWrapped</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">SomeBetterLib</span><span class="p">.</span><span class="nx">getSomething</span><span class="p">()</span> <span class="k">as</span> <span class="kr">number</span> <span class="o">|</span> <span class="nx">boolean</span> <span class="o">|</span> <span class="kr">string</span><span class="p">;</span> <span class="c1">// to deduce this type I had to look at the code or test it multiple times</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now I can use <code class="language-plaintext highlighter-rouge">getSomethingWrapped</code> without using any type assertions (“cast”).</p>

<p>So, to sum up: never use any. Use <code class="language-plaintext highlighter-rouge">unkown</code> instead.</p>

<p>Even the standard library makes this mistakes with <code class="language-plaintext highlighter-rouge">JSON.parse()</code></p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">j</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="dl">"</span><span class="s2">{}</span><span class="dl">"</span><span class="p">);</span> <span class="c1">// j is any here which means I can do</span>
<span class="nx">j</span><span class="p">.</span><span class="nx">lol</span><span class="p">();</span>
<span class="nx">j</span><span class="p">.</span><span class="nx">hello</span><span class="p">();</span> <span class="c1">// without error, here the example si simple enough and we know it will fail at runtime but if JSON.parse() returned unknown we could not do this.</span>

<span class="kd">function</span> <span class="nx">JsonParseWrapped</span><span class="p">(</span><span class="nx">jsonString</span><span class="p">:</span> <span class="kr">string</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">jsonString</span><span class="p">)</span> <span class="k">as</span> <span class="nx">unknown</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">let</span> <span class="nx">jj</span> <span class="o">=</span> <span class="nx">JsonParseWrapped</span><span class="p">(</span><span class="dl">"</span><span class="s2">{}</span><span class="dl">"</span><span class="p">);</span> <span class="c1">// j is any here which means I can do</span>
<span class="nx">jj</span><span class="p">.</span><span class="nx">lol</span><span class="p">();</span> <span class="c1">// error</span>
<span class="nx">jj</span><span class="p">.</span><span class="nx">hello</span><span class="p">();</span> <span class="c1">// error</span>
<span class="p">(</span><span class="nx">jj</span> <span class="k">as</span> <span class="kr">number</span><span class="p">).</span><span class="nx">toExponential</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// works for the compiler but will obviously fail at runtime</span>
</code></pre></div></div>

<h2 id="the-typessript-as-operator">The TypesSript “as” operator</h2>

<p>Now let’s talk about “as”. You probably know that TypeScript is compiled (or transpiled) to JavaScript. You probably know that most of what makes TypeScript is stripped away during this transpilation step which means that, at runtime, TypeScript DOES NOT exists. The “as” operator doesn’t exists in JavaScript, it’s only a TypeScript notion. You can verify it easily.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nx">jj</span> <span class="k">as</span> <span class="kr">number</span><span class="p">).</span><span class="nx">toExponential</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// TypeScript code</span>
<span class="nx">jj</span><span class="p">.</span><span class="nx">toExponential</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="c1">// transpiled JavaScript code of the previous line, "as" is nowhere to be seen</span>
<span class="c1">// That's why I said that "as" is like saying to the TypeScript compiler "I mostly know what I'm doing", why mostly? There is some checks done.</span>

<span class="kd">type</span> <span class="nx">X</span> <span class="o">=</span> <span class="p">{</span> <span class="na">x</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span>
<span class="kd">type</span> <span class="nx">Y</span> <span class="o">=</span> <span class="p">{</span> <span class="na">y</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span> <span class="p">}</span>
<span class="kd">let</span> <span class="nx">x</span><span class="p">:</span> <span class="nx">X</span> <span class="o">=</span> <span class="p">{</span> <span class="na">x</span><span class="p">:</span> <span class="dl">"</span><span class="s2">a</span><span class="dl">"</span> <span class="p">};</span>
<span class="kd">let</span> <span class="nx">y</span> <span class="o">=</span> <span class="nx">x</span> <span class="k">as</span> <span class="nx">Y</span><span class="p">;</span> <span class="c1">// we have an error here "Conversion of type 'X' to type 'Y' may be a mistake because neither type sufficiently overlaps [...]", basically TypeScript it telling that X and Y have nothing in common so it may be a mistake</span>
<span class="kd">let</span> <span class="nx">yy</span> <span class="o">=</span> <span class="nx">x</span> <span class="k">as</span> <span class="nx">unknown</span> <span class="k">as</span> <span class="nx">Y</span><span class="p">;</span> <span class="c1">// but this does not give a compile error, it will still give a runtime error when you try to access yy.a.charAt but at least you have to really want it!</span>

<span class="kd">type</span> <span class="nx">Z</span> <span class="o">=</span> <span class="p">{</span> <span class="na">x</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="nl">z</span><span class="p">:</span> <span class="nx">boolean</span><span class="p">;</span> <span class="p">}</span>
<span class="kd">let</span> <span class="nx">z</span> <span class="o">=</span> <span class="nx">x</span> <span class="k">as</span> <span class="nx">Z</span><span class="p">;</span> <span class="c1">// but here it's ok because X and Z have some fields in common, it will still fail at runtime though</span>
</code></pre></div></div>

<p>But again, this is “as” only exists at compile time which means that if you make a wrong type assertion it will fail at runtime</p>

<p>Checking that you can safely convert a type into another is a complicated problem in the scope of data validation or input validation which I haven’t explored enough to talk about.
```</p>]]></content><author><name></name></author><category term="typescript" /><category term="programming" /><category term="en" /><summary type="html"><![CDATA[(You can also read the whole article on the TypeScript playground.)]]></summary></entry><entry xml:lang="en"><title type="html">Addressing Martial Disparity with One-Time Heroic Actions</title><link href="https://playest.net/blog/2021/04/29/Addressing-Martial-Disparity-with-One-Time-Heroic-Actions.html" rel="alternate" type="text/html" title="Addressing Martial Disparity with One-Time Heroic Actions" /><published>2021-04-29T00:00:00+02:00</published><updated>2021-04-29T00:00:00+02:00</updated><id>https://playest.net/blog/2021/04/29/Addressing%20Martial%20Disparity%20with%20One-Time%20Heroic%20Actions</id><content type="html" xml:base="https://playest.net/blog/2021/04/29/Addressing-Martial-Disparity-with-One-Time-Heroic-Actions.html"><![CDATA[<p>A few month ago <a href="https://www.youtube.com/channel/UChSBq3h26QNYGBmi2yQPHKA">Dael Kingsmill</a> published a <a href="https://www.youtube.com/watch?v=GaGhwXluQo0">video for a champion fix</a>. It’s more of a fighter fix in my opinion. I like what she proposes and it reminded me of my project to improve martial classes. Talking about the fighter Dael says “I just keep hoping that this time, this time, maybe they’ll be what they’re meant to be”. This is something I felt for a long time, it’s true about fighters and other martial classes in my opinion: they are too monotonous in combat and they have less narrative power outside of combat. Obviously this is my opinion, if you disagree you may find all of this very uninteresting.</p>

<p>I want to get the some of the feeling of spells, choose amongst a wide variety of options, do something different gameplay-wise, do something amazing! But I don’t want it to feel like an other list of spells and using spell slots. And I want to especially improve out-of-combat game (exploration and social interaction), not combat, combat is fine.</p>

<p>So I started working on this and it was kind of lost in development hell. More recently there was some talk on reddit about <a href="https://www.reddit.com/r/dndnext/comments/mt9ceg/martial_power_disparity_a_case/">martial power disparity</a> and the fact that martials need their own resources similar to spell slots. The concept of heroic actions was already written so I thought I should push through! And here it is.</p>

<p>I asked myself: what are fighters meant to be? Heroes. And what do heroes do?</p>

<ul>
  <li>Ulysses deceives a Cyclop</li>
  <li>Indiana Jones escapes a rolling boulder and punch Hitler</li>
  <li>Aragorn fights a troll and inspire its troops at the gate of Mordor</li>
  <li>Luke Skywalker shot amazingly into the exhaust vent which lead to the Death Star exploding</li>
  <li>Maximus gains the love of the crowd</li>
</ul>

<p>They do that and many other amazing feats. But they mostly do each feat <strong>once</strong>. And that is the idea. A list of Heroic Actions that can only be accomplished once by a character. When it’s done, it’s done and it will never happen again.</p>

<p>So let me introduce …</p>

<h2 id="the-chapter-of-heroes">The Chapter of Heroes</h2>

<p><em>Writers will blacken pages with your stories. Philosophers will analyze your actions. Bards will sing your adventures in taverns around the world. History will be studied through the prism of your fights and speeches. But doomed you will be since nobody remembers second times.</em></p>

<p>If you’re a fighter, barbarian or monk you get access to heroic actions. You heroic rank is equal to your level in the sum of those classes an determines how many heroic actions you have access to. If you gain one level in any other class you loose the ability to gain new heroic actions.</p>

<p>Most heroic action can be used whenever you want without consuming any action or bonus action, there is no limit to the number of heroic action you can use during your turn but be mindful because you can only use each heroic action once. Not once per day, not once per long rest, not once per short rest, <strong>once</strong>.</p>

<p>When you reach heroic rank one you choose your first 5 heroic actions and you gain all the tier 0 heroic actions. Every time your heroic rank increases you gain access to two more heroic actions.</p>

<p>If your heroic rank is less than 4 you only have access to tier 1 heroic actions. When you reach an heroic rank of 5 you gain access to tier 2 heroic actions. At heroic rank 11 you gain access to tier 3 heroic actions.</p>

<p>Every time you use an heroic action you loose it. People will chant about your exploits but you will never be able to use it again. The next time you gain a level you automatically learn the echoed version of this heroic action which can also be used <em>once</em>.</p>

<p>Some of your heroic actions require your target to make a saving throw to resist the effects. The saving throw DC is calculated as follows: 8 + your proficiency bonus + your Strength modifier.</p>

<h2 id="heroic-actions-44">Heroic Actions (44)</h2>

<h3 id="tier-0-1">Tier 0 (1)</h3>

<h4 id="generic-heroic-actions-1">Generic Heroic Actions (1)</h4>

<ol>
  <li><strong>I know.</strong>
 <u></u> You can <u></u> learn any heroic action (that you never learned and never used).
    <ul>
      <li>Echoed: <u>During a short rest,</u> you can <u>spend some time remembering your old adventures and</u> learn any heroic action (that you never learned and never used).</li>
    </ul>
  </li>
</ol>

<h3 id="tier-1-25">Tier 1 (25)</h3>

<h4 id="tactical-heroic-actions-11">Tactical Heroic Actions (11)</h4>

<ol>
  <li><strong>I see you.</strong>
 You can throw <u>any weapon</u> as if it had the thrown property with a range of 200 ft/600 ft.
    <ul>
      <li>Echoed: You can throw <u>a weapon with the thrown property</u> as if it had the thrown property with a range of 200 ft/600 ft.</li>
    </ul>
  </li>
  <li><strong>I can’t Hear You.</strong>
 <u>You smash your head and ears,</u> you are immune to any effect that would sense your emotions or read your thoughts and the charmed condition for the next minute.
    <ul>
      <li>Echoed: <u>As an action you smash your head and ears, you take 2d4 bludgeoning damages and make a constitution saving throw. On a success</u> you are immune to any effect that would sense your emotions or read your thoughts and the charmed condition for the next minute.</li>
    </ul>
  </li>
  <li><strong>Gotta go fast.</strong>
 You can move up to <u>100</u> ft.
    <ul>
      <li>Echoed: You can move up to <u>30</u> ft.</li>
    </ul>
  </li>
  <li><strong>Not on my watch.</strong>
 <u></u> You are no longer surprised.
    <ul>
      <li>Echoed: <u>If you would be surprised make a initiative check,</u> you are no longer surprised <u>if you get more than the best initiative in the group that surprised you.</u></li>
    </ul>
  </li>
  <li><strong>My turn.</strong>
 <u>When you would roll</u> for initiative you can <u>choose to get an initiative of 100 instead.</u>
    <ul>
      <li>Echoed: <u>After you rolled</u> for initiative you can <u>roll an additional d20, you choose which one of the d20 is used.</u></li>
    </ul>
  </li>
  <li><strong>My Head is Bloody, but Unbowed.</strong>
 After failing a weapon attack roll you can <u>choose to succeed instead</u>.
    <ul>
      <li>Echoed: After failing a weapon attack roll you can <u>roll an additional d20, you choose which one of the d20 is used</u>.</li>
    </ul>
  </li>
  <li><strong>Hit Good.</strong>
 For the next minute you can add <u>1d12</u> to all your weapon attack rolls.
    <ul>
      <li>Echoed: For the next minute you can add <u>1d4</u> to all your weapon attack rolls.</li>
    </ul>
  </li>
  <li><strong>Hit Strong.</strong>
 For the next minute you can add <u>1d12</u> to all your weapon damage.
    <ul>
      <li>Echoed: For the next minute you can add <u>1d4</u> to all your weapon damage.</li>
    </ul>
  </li>
  <li><strong>Protection.</strong>
 As a reaction you can move up to <u>30</u> ft and take the place of an other willing character. You push this other character <u>20</u> ft in any direction.
    <ul>
      <li>Echoed: As a reaction you can move up to <u>10</u> ft and take the place of an other willing character. You push this other character <u>10</u> ft in any direction.</li>
    </ul>
  </li>
  <li><strong>Swimming whirlwind.</strong>
 You are so strong that you can create current in the water that can pull enemies toward you or unbalance them if they within <u>30</u> ft of you waist down in the same body of water. They must succeed an athletics check DC 10 + your Athletics bonus or be either pull towards you <u>20</u> ft and/or knocked prone, your choice.
    <ul>
      <li>Echoed: You are so strong that you can create current in the water that can pull enemies toward you or unbalance them if they within <u>10</u> ft of you waist down in the same body of water. They must succeed an athletics check DC 10 + your Athletics bonus or be either pull towards you <u>10</u> ft and/or knocked prone, your choice.</li>
    </ul>
  </li>
  <li><strong>I have the high ground.</strong>
 You gain a climbing speed of <u>40</u> ft for the next 1 minute.
    <ul>
      <li>Echoed: You gain a climbing speed of <u>20</u> ft for the next 1 minute.</li>
    </ul>
  </li>
</ol>

<h4 id="non-tactical-heroic-actions-14">Non-tactical Heroic Actions (14)</h4>

<ol>
  <li><strong>Fear me.</strong>
 You intimidate target creature. They faint.
    <ul>
      <li>Echoed: When you make a successful Charisma (Intimidation) check one of the target faint.</li>
    </ul>
  </li>
  <li><strong>Breathe.</strong>
 You can concentrate for <u>10 seconds</u>, oxygenating your blood. You can then hold your breath for <u>10</u> additional minutes.
    <ul>
      <li>Echoed: You can concentrate for <u>1 minute</u>, oxygenating your blood. You can then hold your breath for <u>1</u> additional minutes.</li>
    </ul>
  </li>
  <li><strong>Who Needs a Ladder?”</strong>
 <u></u> Add <a href="https://en.wikipedia.org/wiki/Men%27s_high_jump_world_record_progression">8 ft</a> to your next high jump.
    <ul>
      <li>Echoed: <u>Make an athletics check DC 15, if you succeed</u> add <a href="https://en.wikipedia.org/wiki/Men%27s_high_jump_world_record_progression">8 ft</a> to your next high jump.</li>
    </ul>
  </li>
  <li><strong>See You on the Other Side.</strong>
 <u></u> Add <a href="https://en.wikipedia.org/wiki/Men%27s_long_jump_world_record_progression">29 ft</a> to your next long jump.
    <ul>
      <li>Echoed: <u>Make an athletics check DC 15, if you succeed</u> add <a href="https://en.wikipedia.org/wiki/Men%27s_long_jump_world_record_progression">29 ft</a> to your next long jump.</li>
    </ul>
  </li>
  <li><strong>Beast of Burden.</strong>
 For the next <u>10 minutes</u> your carrying capacity is increased by <a href="https://en.wikipedia.org/wiki/List_of_Olympic_records_in_weightlifting">476 lb. (216 kg)</a>
    <ul>
      <li>Echoed: For the next <u>minute</u> your carrying capacity is increased by <a href="https://en.wikipedia.org/wiki/List_of_Olympic_records_in_weightlifting">476 lb. (216 kg)</a></li>
    </ul>
  </li>
  <li><strong>Know Your Enemy.</strong>
 <u>You instantly</u> know any two information of your choice about a target within 30 ft amongst: one ability score, level, CR, armor class, current hit point.
    <ul>
      <li>Echoed: <u>After you spent one minute looking at a target you</u> know any two information of your choice about a target within 30 ft amongst: one ability score, level, CR, armor class, current hit point.</li>
    </ul>
  </li>
  <li><strong>I. Don’t. Fail.</strong>
 If you would fail at a Dexterity (Acrobatics) check you can <u>choose to succeed instead</u>.
    <ul>
      <li>Echoed: If you would fail at a Dexterity (Acrobatics) check you can <u>roll an additional d20, you choose which one of the d20 is used</u>.</li>
      <li><em>This heroic action has several version: one per existing skill, you gain access to each of them separately.</em></li>
    </ul>
  </li>
  <li><strong>Resist.</strong>
 If you would fail at a Strength saving throw you can <u>choose to succeed instead</u>.
    <ul>
      <li>Echoed: If you would fail at a Strength saving throw you can <u>roll an additional d20, you choose which one of the d20 is used</u>.</li>
      <li><em>This heroic action has several version: one per saving ability, you gain access to each of them separately.</em></li>
    </ul>
  </li>
  <li><strong>Skill Good.</strong>
 For the next minute you can add <u>1d12</u> to all your ability checks.
    <ul>
      <li>Echoed: For the next minute you can add <u>1d4</u> to all your ability checks.</li>
    </ul>
  </li>
  <li><strong>War is its Own Language.</strong>
 By spending one minute interacting peacefully with a creature with at least one language you can then use sounds and gestures to talk to it as if you had proficiency in that creature language. <u></u>
    <ul>
      <li>Echoed: By spending one minute interacting peacefully with a creature with at least one language you can then use sounds and gestures to talk to it as if you had proficiency in that creature language. <u>It works as long as what you’re saying is not too complex (at the DM discretion).</u></li>
    </ul>
  </li>
  <li><strong>Grift with Confidence.</strong>
(inspired by <a href="https://drive.google.com/file/d/0B40nce9YZ1MbTDJsVjh0ZFpXenc/view">Thirteen Expanded Feats</a>/)
 If you succeed on a Charisma (Deception) check against a creature, the next Charisma (Deception) check you make receives a <u>1d8</u> bonus if it is made within 10 minutes of the previous check.
    <ul>
      <li>Echoed: If you succeed on a Charisma (Deception) check against a creature, the next Charisma (Deception) check you make receives a <u>1d4</u> bonus if it is made within 10 minutes of the previous check.</li>
    </ul>
  </li>
  <li><strong>You can run…</strong>
 By focusing your senses you gain blindsight (15 ft) for <u>10</u> minutes.
    <ul>
      <li>Echoed: By focusing your senses you gain blindsight (15 ft) for <u>1</u> minutes.</li>
    </ul>
  </li>
  <li><strong>Martial Command.</strong>
(inspired by <a href="https://dnd.wizards.com/articles/features/basicrules">Player’s Handbook</a>/Command spell)
 You speak a one-word command to a creature you can see within 60 ft. The target must <u></u> follow the command on its next turn. This command has no effect if the target doesn’t understand your language, or if your command is directly harmful to it.
    <ul>
      <li>Echoed: You speak a one-word command to a creature you can see within 60 ft. The target must <u>succeed on a Wisdom saving throw or</u> follow the command on its next turn. This command has no effect if the target doesn’t understand your language, or if your command is directly harmful to it.</li>
    </ul>
  </li>
  <li><strong>Choose Your Battles.</strong>
 You attempt to calm a group of people. <u>Choose one of the following two effects</u>. You can remove any effect causing a target to be frightened or charmed. Alternatively, you can make a target indifferent about any creatures of your choice. The effect of this feature last while you maintain a conversation with the targets with a maximum of <u>one hour</u>.
    <ul>
      <li>Echoed: You attempt to calm a group of people. <u>Each creature that can hear and understand you must make a Charisma saving throw; a creature can choose to fail this saving throw if it wishes. If a creature fails its saving throw, choose one of the following two effects</u>. You can remove any effect causing a target to be frightened or charmed. Alternatively, you can make a target indifferent about any creatures of your choice. The effect of this feature last while you maintain a conversation with the targets with a maximum of <u>one minute</u>.</li>
    </ul>
  </li>
</ol>

<h3 id="tier-2-12">Tier 2 (12)</h3>

<h4 id="tactical-heroic-actions-6">Tactical Heroic Actions (6)</h4>

<ol>
  <li><strong>I will not yield.</strong>
 <u>Instead of rolling</u> a death saving throw you regain 1HP. You can then act normally.
    <ul>
      <li>Echoed: <u>If you succeeded at</u> a death saving throw you regain 1HP. You can then act normally.</li>
    </ul>
  </li>
  <li><strong>Again. And again!</strong>
 On your turn, you can take <u>two</u> additional actions on top of your regular action and <u>two</u> possible bonus action.
    <ul>
      <li>Echoed, <strong>Again</strong>: On your turn, you can take <u>one</u> additional actions on top of your regular action and <u>one</u> possible bonus action.</li>
    </ul>
  </li>
  <li><strong>Aware.</strong>
 For the next <u>hour</u> you can’t be surprised and have advantage on weapon attack rolls, ability checks, and saving throws. Additionally, other creatures have disadvantage on weapon attack rolls against you for the duration.
    <ul>
      <li>Echoed: For the next <u>minute</u> you can’t be surprised and have advantage on weapon attack rolls, ability checks, and saving throws. Additionally, other creatures have disadvantage on weapon attack rolls against you for the duration.</li>
    </ul>
  </li>
  <li><strong>No.</strong>
 Counter a spell if the caster is within your weapon reach.
    <ul>
      <li>Echoed: Counter a spell if the caster is within your weapon reach <u>and if the caster fails a constitution saving throw against your weapon DC</u>.</li>
    </ul>
  </li>
  <li><strong>In The Face.</strong>
 Your next attack is a critical hit.
    <ul>
      <li>Echoed: Your next attack is a critical hit <u>on a 12 or above</u>.</li>
    </ul>
  </li>
  <li><strong>Look me in the Eyes.</strong>
 Each creature in a 30-foot cone <u></u> drop whatever it is holding and become frightened for 3 rounds. While frightened by this effect, a creature must take the Dash action and try to move away from you on each of its turns. <u></u>
    <ul>
      <li>Echoed: Each creature in a 30-foot cone <u>must succeed on a Wisdom saving throw or</u> drop whatever it is holding and become frightened for 3 rounds. While frightened by this effect, a creature must take the Dash action and try to move away from you on each of its turns. <u>At the end of its turn if the creature doesn't have line of sight to you it can make a Wisdom saving throw which ends the effect on a success.</u></li>
    </ul>
  </li>
</ol>

<h4 id="non-tactical-heroic-actions-6">Non-tactical Heroic Actions (6)</h4>

<ol>
  <li><strong>Working Paragon.</strong>
 Something that would take up to <u>1 week</u> to do with tools you are proficient with can be done <u>7</u> times as fast.
    <ul>
      <li>Echoed: Something that would take up to <u>1 day</u> to do with tools you are proficient with can be done <u>3</u> times as fast.</li>
    </ul>
  </li>
  <li><strong>I am the Master of my fate.</strong>
 After failing a ability check you can <u>choose to succeed instead</u>.
    <ul>
      <li>Echoed: After failing a ability check you can <u>roll an additional d20, you choose which one of the d20 is used</u>.</li>
    </ul>
  </li>
  <li><strong>I am the Captain of my Soul.</strong>
 After failing a saving throw you can <u>choose to succeed instead</u>.
    <ul>
      <li>Echoed: After failing a saving throw you can <u>roll an additional d20, you choose which one of the d20 is used</u>.</li>
    </ul>
  </li>
  <li><strong>Jump.</strong>
 You gain a flying speed equal to <u>twice</u> your walking speed for <u>2 round</u>. <u>After these 2 rounds you lose your vertical momentum and can't go up, you can still go straight or go down for one round. Then you fall.</u>
    <ul>
      <li>Echoed: You gain a flying speed equal to <u></u> your walking speed for <u>1 round</u>. <u></u></li>
    </ul>
  </li>
  <li><strong>Sword to Plowshare.</strong>
 In a impossible feat of strength and endurance you spend <u>8 hours</u> working in the fields around you doing as much as dozen of other people. You enrich the land in a half-mile radius for 1 year. The plants yield twice the normal amount of food when harvested.
    <ul>
      <li>Echoed: In a impossible feat of strength and endurance you spend <u>a week</u> working in the fields around you doing as much as dozen of other people. You enrich the land in a half-mile radius for 1 year. The plants yield twice the normal amount of food when harvested.</li>
    </ul>
  </li>
  <li><strong>Sage Advice</strong>
(inspired by <a href="https://www.gmbinder.com/share/-L8hC0Kqh4yvXSY_v3aB">The Scholar</a>/Sage Advice and Theoretical Advice)
 You may spread your knowledge and experience to those around you. You spend 1 minute advising your companions. When you do so, choose a skill or tool <u></u> and a number of friendly creatures up to your Intelligence modifier who can understand, hear, and see you. <u>Twice</u> within the next hour, the next time each creature would make a check of the chosen skill or tool, they may add their proficiency modifier to the roll, or double their proficiency if they are already proficient.
    <ul>
      <li>Echoed: You may spread your knowledge and experience to those around you. You spend 1 minute advising your companions. When you do so, choose a skill or tool <u>you are proficient with</u> and a number of friendly creatures up to your Intelligence modifier who can understand, hear, and see you. <u>Once</u> within the next hour, the next time each creature would make a check of the chosen skill or tool, they may add their proficiency modifier to the roll, or double their proficiency if they are already proficient.</li>
    </ul>
  </li>
</ol>

<h3 id="tier-3-6">Tier 3 (6)</h3>

<h4 id="non-tactical-heroic-actions-3">Non-tactical Heroic Actions (3)</h4>

<ol>
  <li><strong>Never Tell me the Odds.</strong>
(inspired by <a href="https://www.dmsguild.com/product/233170/">Balsar’s Guide to Exploration</a>/Foretell the Odds)
 Whenever a creature within range of your weapon makes an attack roll, ability check or saving throw, you may expend expand your reaction to have their roll become <u>2 or 18</u> (before any modifiers are applied). You can choose to do this after the roll, but before the dice outcome is deemed to be a success or failure.
    <ul>
      <li>Echoed: Whenever a creature within range of your weapon makes an attack roll, ability check or saving throw, you may expend expand your reaction to have their roll become <u>10</u> (before any modifiers are applied). You can choose to do this after the roll, but before the dice outcome is deemed to be a success or failure.</li>
    </ul>
  </li>
  <li><strong>I’m on a Roll.</strong>
 You have advantage at all you ability checks for the <u>next hours</u> or until you fail one, whichever comes first.
    <ul>
      <li>Echoed: You have advantage at all you ability checks for the <u>next minute</u> or until you fail one, whichever comes first.</li>
    </ul>
  </li>
  <li><strong>You Will Listen.</strong>
 If you were not behaving in an aggressive way any person within 30 ft that can see you is now <u>paralyzed</u> as long as you do not attack or for <u>one minute</u>, whichever comes first. <u><u>You get advantage on any Charisma throw you could make during this time.</u></u>
    <ul>
      <li>Echoed: If you were not behaving in an aggressive way any person within 30 ft that can see you is now <u>stunned</u> as long as you do not attack or for <u>10 seconds</u>, whichever comes first. <u></u></li>
    </ul>
  </li>
</ol>

<h4 id="tactical-heroic-actions-3">Tactical Heroic Actions (3)</h4>

<ol>
  <li><strong>You’re going down.</strong>
 For the duration of this round all your attacks <u>are critical hits</u>.
    <ul>
      <li>Echoed: For the duration of this round all your attacks <u>hit (you can still roll the dice in case of a critical hit)</u>.</li>
    </ul>
  </li>
  <li><strong>'Tis But a Scratch.</strong>
 You regain <u>all</u> your HP.
    <ul>
      <li>Echoed: You regain <u>half</u> your HP.</li>
    </ul>
  </li>
  <li><strong>Won’t Go Down.</strong>
 As a reaction you can <u>reduce to zero</u> all the damage you would take during this round.
    <ul>
      <li>Echoed: As a reaction you can <u>halve</u> all the damage you would take during this round.</li>
    </ul>
  </li>
</ol>

<h3 id="optional-rules">Optional Rules</h3>

<h4 id="rogue-can-do-it-too">Rogue Can do it Too</h4>

<p>Normally Rogues don’t get access to heroic actions. With this optional rule they do.</p>

<h4 id="average-heroic-rank">Average Heroic Rank</h4>

<p>If you decide to use this optional rule your heroic rank is determined by your level and your class instead of the standard way. Classes are divided in 4 categories:</p>
<ul>
  <li><strong>Full-casters</strong> (wizards, druids, bards, clerics, sorcerers) that have a <strong>9th level spell slot</strong> as their higher level spell slot</li>
  <li><strong>Half-casters</strong> (paladins, rangers, artificers, and for the purpose of this module, warlocks) that have a <strong>5th level spell slot</strong> as their higher level spell slot</li>
  <li><strong>Third-casters</strong> (fighters/eldritch knights, rogue/arcane trickster) that have a <strong>4th level spell slot</strong> as their higher level spell slot</li>
  <li><strong>Non-casters</strong> (fighters, barbarian, monk, rogue) that have no spell slots</li>
</ul>

<p>You heroic rank is equal to (1/2 × the sum of your levels in half-caster classes rounded up) + (2/3 × the sum of you levels in third-caster classes rounded up) + the sum of your levels in non-caster classes.</p>

<h4 id="magic-spoils-everything">Magic Spoils Everything</h4>

<p>If you decide to use this optional rule your heroic rank is determined by your level and your class with the highest magic. If you only have levels in non-caster classes or third-caster classes you heroic rank is determined normally. If you have at least one level in a half-caster class then your heroic rank is halved. If you have at least one level in a full-caster class you have no heroic rank (and hence no heroic actions).</p>

<h2 id="closing-remarks">Closing Remarks</h2>

<p>I’m not so sure about many things here. Why only 5 heroic action at the beginning and two more at each rank? I don’t know maybe it’s too much? Maybe not?</p>

<p>I like that the player is encouraged to use the action because otherwise the echoed is kind of “lost” but at the same time she is encouraged to keep it for when it really matters.</p>

<p>I’m not so sure about the division in tier. Maybe I should put everything in the same tier? Maybe tier 2 actions are better than tier 3?</p>

<p>Also maybe echoed actions should be stronger than heroic actions? That would give more incentive to use it.</p>

<p>There might be typos also, feel free to leave a comment about hat and I’ll fix them.</p>

<p>Anyway, I might update this article or add new actions in the future.</p>]]></content><author><name></name></author><category term="dnd" /><category term="dnd5" /><category term="ttrpg" /><category term="en" /><summary type="html"><![CDATA[A few month ago Dael Kingsmill published a video for a champion fix. It’s more of a fighter fix in my opinion. I like what she proposes and it reminded me of my project to improve martial classes. Talking about the fighter Dael says “I just keep hoping that this time, this time, maybe they’ll be what they’re meant to be”. This is something I felt for a long time, it’s true about fighters and other martial classes in my opinion: they are too monotonous in combat and they have less narrative power outside of combat. Obviously this is my opinion, if you disagree you may find all of this very uninteresting.]]></summary></entry><entry xml:lang="fr"><title type="html">Redonner de l’intérêt aux combattant avec les Actions Héroïques Unique</title><link href="https://playest.net/blog/2021/04/29/Redonner-de-l'int%C3%A9r%C3%AAt-aux-combattant-avec-les-Actions-H%C3%A9ro%C3%AFques.html" rel="alternate" type="text/html" title="Redonner de l’intérêt aux combattant avec les Actions Héroïques Unique" /><published>2021-04-29T00:00:00+02:00</published><updated>2021-04-29T00:00:00+02:00</updated><id>https://playest.net/blog/2021/04/29/Redonner%20de%20l&apos;int%C3%A9r%C3%AAt%20aux%20combattant%20avec%20les%20Actions%20H%C3%A9ro%C3%AFques</id><content type="html" xml:base="https://playest.net/blog/2021/04/29/Redonner-de-l&apos;int%C3%A9r%C3%AAt-aux-combattant-avec-les-Actions-H%C3%A9ro%C3%AFques.html"><![CDATA[<p>Il y a quelques mois <a href="https://www.youtube.com/channel/UChSBq3h26QNYGBmi2yQPHKA">Dael Kingsmill</a> a publié une <a href="https://www.youtube.com/watch?v=GaGhwXluQo0">video</a> essayant de donner un peu plus d’intérêt au champion. Ça concerne plutôt l’entiéreté de la classe Guerrier à mon avis. J’aime bien ce qu’elle a proposé et ça m’a rappelé que j’avais pour projet d’essayer de rendre les classes martiales plus intéressantes. En parlant du guerrier Dael dit “J’espère que, enfin, cette fois, ils seront ce qu’il doivent être”. C’est quelque chose auquel je suis sensible et que je ressent depuis assez longtemps, c’est vrai pour les guerrier mais aussi des autres classe martiales : elle sont très monotones en dehors des combats. De toute évidente c’est mon opinion, et si vous n’êtes pas d’accord vous allez trouver tout ceci très inintéressant.</p>

<p>Je veux avoir quelque chose qui ressemble à des sorts, qui permet de choisir dans une longue liste d’option, qui est différent en terme de gameplay, et qui est extraordinaire ! Mais je ne veux pas que ça ressemble a une énième liste de sorts ni que ça utilise des emplacements de sorts. Et je veux en particulier améliorer l’expérience hors-combat (exploration et social), en combat je trouve que ça va.</p>

<p>Donc, j’ai commencé à travailler sur ce projet et il s’est un peu perdu dans les limbes … Récemment des gens ont parlé de la <a href="https://www.reddit.com/r/dndnext/comments/mt9ceg/martial_power_disparity_a_case/">disparité des classe martiales</a> sur Reddit et du fait que les combattants ont besoin d’avoir leur propre ressource similaire aux emplacements de sorts. J’avais déjà pensé aux concept des actions héroïques donc je me suis dit que j’allais essayé de terminer. Et voilà ce que j’ai pondu !</p>

<p>Je me suis demandé : “Qu’est-ce que les combattants sont sensé être ? Des héros. Et que font les héros ?”</p>

<ul>
  <li>Ulysse trompe le Cyclop</li>
  <li>Indiana Jones échappe à un boulet qui aurait pu l’écraser et donne un coup de poing à Hitler</li>
  <li>Aragorn combat un troll et fait un discour inspirant à son armé devant les portes du Mordor</li>
  <li>Luke Skywalker fait un tire précis dans la bouche d’entrée ce qui fait explosé l’étoile de la mort</li>
  <li>Maximus impressione la foule et gagne son soutien</li>
</ul>

<p>Ils font ça et bien d’autres choses. Mais ils font chacune des ces choses <strong>une fois</strong>. Et c’est ça l’idée. Une liste d’action héroïque qui ne peuvent être faite qu’une fois par personnage. Quand c’est fait, c’est fini !</p>

<p>Laissez moi donc vous présenter …</p>

<h2 id="le-chapitre-des-héros">Le Chapitre des Héros</h2>

<p><em>Les écrivains noircirons les pages avec vos histoires. Les philosophes analyserons vos actions. Les bardes chanteront vos aventures dans les tavernes tout autour du monde. L’histoire sera étudiée à travers le prisme de combat et de vos discours. Mais vous serez maudit car personne ne se souvient des 2ème fois.</em></p>

<p>Si vous êtes un guerrier, un barabare ou un moine vous avec accès aux actions héroïques. Vos range de héro est égale à la somme de vos niveaux dans ces classes et determine à combien d’actions héroïques vous avez accès. Si vous gagnez un niveau dans une autre classe classe vous perdez la possibilité de gagner de nouvelles actions héroïques.</p>

<p>La plupart des actions heroïques peuvent être utilisé à n’importe quel moment sans consommer d’action ou d’action bonus, il n’y a pas de limite au nombre d’action héroïques que vous pouvez utiliser pendant votre tour mais soyez économe car vous ne pouvez utiliser chaque action héroïque qu’une fois. Pas une fois par jour, pas une fois par repos long, pas une fois pas repos court, <strong>une fois</strong>.</p>

<p>Quand vous atteignez le rang de héros 1 vous choisissez vos 5 premières actions héroïques et vous apprenez en plus toutes les actions héroïque de tier 0. À chaque fois que vous gagnez un rang de héros vous apprennez 2 nouvelles actions héroïques.</p>

<p>Si votre rang de héros est de 4 ou moins vous avec uniquement accès au actions héroïques de tier 1. Quand vous atteigner le rang de héros 5 vous avez accès au actions de tier 2. Au rang de héros 11 vous avez accès au tier 3 des actions heroïques.</p>

<p>À chaque vous que vous utiliser une action héroïque vous ne pouvez plus l’utiliser à nouveau. Les gens chanterons vos exploits mais vous ne pourrez plus jamais refaire cet exploit. La prochaine fois que vous gagnez un niveau vous apprenez automatiquement le version echo de cette action héroïque qui ne peut, elle aussi, être utilisé qu’une fois.</p>

<p>Quelques une de vos actions héroïques necessite que la cible fasse un jet de sauvegarde. Le DD est calculé de la manière suivante : 8 + votre bonus de maîtrise + votre modificateur de force.</p>

<h2 id="actions-héroïques-44">Actions Héroïques (44)</h2>

<h3 id="tier-0-1">Tier 0 (1)</h3>

<h4 id="actions-héroïques-générique-1">Actions Héroïques Générique (1)</h4>

<ol>
  <li><strong>Je sais.</strong>
 <u></u> Vous pouvez <u></u> apprendre n’importe quelle actions héroïque (que vous n’avez jamais apprise et jamais utilisé).
    <ul>
      <li>Echoed: <u>Lors d'un repos court,</u> vous pouvez <u>passez un peu de temps à vous remémorez vos vielles aventures et</u> apprendre n’importe quelle actions héroïque (que vous n’avez jamais apprise et jamais utilisé).</li>
    </ul>
  </li>
</ol>

<h3 id="tier-1-25">Tier 1 (25)</h3>

<h4 id="actions-héroïques-tactique-11">Actions Héroïques Tactique (11)</h4>

<ol>
  <li><strong>Je te vois.</strong>
 Vous pouvez lancer <u>n'importe quelle arme</u> comme s’il avait la propriété lancer avec une portée de 200 ft/600 ft.
    <ul>
      <li>Echoed: Vous pouvez lancer <u>une arme avec la propriété lancer</u> comme s’il avait la propriété lancer avec une portée de 200 ft/600 ft.</li>
    </ul>
  </li>
  <li><strong>Je ne t’entends pas.</strong>
 <u>Vous frappez vos oreilles,</u> vous êtes immunisez à n’importe quelle effet qui devrait sentir vos émotions ou lire vos pensées ainsi qu’à la condition charmé pendant une minute.
    <ul>
      <li>Echoed: <u>En tant qu'action vous frappez vos oreilles, vous subissez 2d4 dégât contondants et faites un jet de sauvegarde de constitution. Si c'est un succès</u> vous êtes immunisez à n’importe quelle effet qui devrait sentir vos émotions ou lire vos pensées ainsi qu’à la condition charmé pendant une minute.</li>
    </ul>
  </li>
  <li><strong>Plus vite.</strong>
 Vous pouvez vous déplacer de <u>100</u> pieds.
    <ul>
      <li>Echoed: Vous pouvez vous déplacer de <u>30</u> pieds.</li>
    </ul>
  </li>
  <li><strong>Pas si je peux l’empêcher.</strong>
 <u></u> Vous n’être plus surpris.
    <ul>
      <li>Echoed: <u>Si vous deviez être surpris faite un jet d'initiative,</u> vous n’être plus surpris <u>Si vous avez plus d'initiative que la plus haute des initiative dans le groupe qui vous a surpris.</u></li>
    </ul>
  </li>
  <li><strong>À mon tour.</strong>
 <u>Quand vous devriez lancer</u> l’inititive vous pouvez <u>choisissez d'avoir une inititive de 100 à la place.</u>
    <ul>
      <li>Echoed: <u>Après avoir lancé</u> l’inititive vous pouvez <u>lancez un d20 en plus, vous choisissez quel résultat utiliser.</u></li>
    </ul>
  </li>
  <li><strong>Ma tête est blessé mais haute.</strong>
 Après avoir échoué un jet d’attaque avec une arme vous pouvez <u>choisir de réussir à la place</u>.
    <ul>
      <li>Echoed: Après avoir échoué un jet d’attaque avec une arme vous pouvez <u>lancer un d20 supplémentaire, vous choisissez quel résultat utiliser</u>.</li>
    </ul>
  </li>
  <li><strong>Frapper Bien.</strong>
 Pendant une minute vous pouvez ajouter <u>1d12</u> à tout vos jets d’attaque avec des armes.
    <ul>
      <li>Echoed: Pendant une minute vous pouvez ajouter <u>1d4</u> à tout vos jets d’attaque avec des armes.</li>
    </ul>
  </li>
  <li><strong>Frapper Fort.</strong>
 Pendant une minute vous pouvez ajouter <u>1d12</u> à tous vos jets de dégâts.
    <ul>
      <li>Echoed: Pendant une minute vous pouvez ajouter <u>1d4</u> à tous vos jets de dégâts.</li>
    </ul>
  </li>
  <li><strong>Protection.</strong>
 En tant que réaction vous pouvez vous déplacer de <u>30</u> pieds et prendre la place d’une autre personnage volontaire. Vous poussez ce personnage <u>20</u> pieds dans la direction de votre choix.
    <ul>
      <li>Echoed: En tant que réaction vous pouvez vous déplacer de <u>10</u> pieds et prendre la place d’une autre personnage volontaire. Vous poussez ce personnage <u>10</u> pieds dans la direction de votre choix.</li>
    </ul>
  </li>
  <li><strong>Nage Tourbillonante.</strong>
 Vous êtes si fort que vous pouvez crééer un courant dans l’eau qui peut transporter ou destabiliser vos enemies s’ils sont à moins de <u>30</u> pieds de vous et au moins jusqu’à la taille dans la même étendue d’eau. Ils doivent réussir un jet de Force (Athelisme) DD 10 + votre bonus d’Athelisme or être soit tiré vers vous de <u>20</u> pieds et/ou être mis à terre, c’est vous qui choisissez.
    <ul>
      <li>Echoed: Vous êtes si fort que vous pouvez crééer un courant dans l’eau qui peut transporter ou destabiliser vos enemies s’ils sont à moins de <u>10</u> pieds de vous et au moins jusqu’à la taille dans la même étendue d’eau. Ils doivent réussir un jet de Force (Athelisme) DD 10 + votre bonus d’Athelisme or être soit tiré vers vous de <u>10</u> pieds et/ou être mis à terre, c’est vous qui choisissez.</li>
    </ul>
  </li>
  <li><strong>Je te Domine.</strong>
 Vous gagner une vitesse d’escalade de <u>40</u> pieds pendant 1 minute.
    <ul>
      <li>Echoed: Vous gagner une vitesse d’escalade de <u>20</u> pieds pendant 1 minute.</li>
    </ul>
  </li>
</ol>

<h4 id="actions-héroïques-non-tactique-14">Actions Héroïques Non-tactique (14)</h4>

<ol>
  <li><strong>Tu devrais avoir peur de moi.</strong>
 vous intimidez la cible. Elle s’évanouie.
    <ul>
      <li>Echoed: Quand vous réussisez un jet de Charisme (Intimidation) une des cible s’évanouie.</li>
    </ul>
  </li>
  <li><strong>Respire.</strong>
 Vous pouvez vous concentrer pendant <u>10 secondes</u>, oxygéant votre sang. Vous pouvez retenir votre respiration pour <u>10</u> minutes supplémentaires.
    <ul>
      <li>Echoed: Vous pouvez vous concentrer pendant <u>1 minute</u>, oxygéant votre sang. Vous pouvez retenir votre respiration pour <u>1</u> minutes supplémentaires.</li>
    </ul>
  </li>
  <li><strong>Les échelles sont inutiles</strong>
 <u></u> Ajoutez <a href="https://en.wikipedia.org/wiki/Men%27s_high_jump_world_record_progression">8 pieds</a> à votre prochain saut en hauteur.
    <ul>
      <li>Echoed: <u>Faites un jet de Force (Athlétisme) DD 15, si vous réussissez</u> ajoutez <a href="https://en.wikipedia.org/wiki/Men%27s_high_jump_world_record_progression">8 pieds</a> à votre prochain saut en hauteur.</li>
    </ul>
  </li>
  <li><strong>On se voit de l’autre côté.</strong>
 <u></u> Ajoutez <a href="https://en.wikipedia.org/wiki/Men%27s_long_jump_world_record_progression">29 ft</a> à votre prochain saut en longeur.
    <ul>
      <li>Echoed: <u>Faites un jet d'Athletisme DD 15, si vous réussissez</u> ajoutez <a href="https://en.wikipedia.org/wiki/Men%27s_long_jump_world_record_progression">29 ft</a> à votre prochain saut en longeur.</li>
    </ul>
  </li>
  <li><strong>Bête de bat.</strong>
 Pendant <u>10 minutes</u> vous pouvez portez <a href="https://en.wikipedia.org/wiki/List_of_Olympic_records_in_weightlifting">476 lb. (216 kg)</a> de plus.
    <ul>
      <li>Echoed: Pendant <u>1 minute</u> vous pouvez portez <a href="https://en.wikipedia.org/wiki/List_of_Olympic_records_in_weightlifting">476 lb. (216 kg)</a> de plus.</li>
    </ul>
  </li>
  <li><strong>Connais ton ennemi.</strong>
 <u>Instantanément, vous</u> apprennez deux informations de votre choix à propos d’une cible qui est à moins de 30 pieds de vous parmi: un score de caractéristique, niveau, CR, classe d’armure, point de vie actuel.
    <ul>
      <li>Echoed: <u>Après avoir passez une minute à observer une cible vous</u> apprennez deux informations de votre choix à propos d’une cible qui est à moins de 30 pieds de vous parmi: un score de caractéristique, niveau, CR, classe d’armure, point de vie actuel.</li>
    </ul>
  </li>
  <li><strong>Je. N’échoue. Pas.</strong>
 Si vous deviez échouer à un jet de Dextérité (Acrobatie) vous pouvez <u>choisir de réussir à la place</u>.
    <ul>
      <li>Echoed: Si vous deviez échouer à un jet de Dextérité (Acrobatie) vous pouvez <u>lancer un d20 supplémentaire, vous choisissez quel résultat utiliser</u>.</li>
      <li><em>Cette action héroïque a plusieurs versions: une par compétence, vous apprennez chacune d’entre elles séparément.</em></li>
    </ul>
  </li>
  <li><strong>Resiste.</strong>
 Si vous deviez échouer à un jet de sauvegarde de force vous pouvez <u>choisir de réussir à la place</u>.
    <ul>
      <li>Echoed: Si vous deviez échouer à un jet de sauvegarde de force vous pouvez <u>lancer un d20 supplémentaire, vous choisissez quel résultat utiliser</u>.</li>
      <li><em>Cette action héroïque a plusieurs versions: une par jet de sauvegarde, vous apprennez chacune d’entre elles séparément.</em></li>
    </ul>
  </li>
  <li><strong>Compétence Bien.</strong>
 Pendant une minute vous pouvez ajouter <u>1d12</u> à tout vos jet de compétence.
    <ul>
      <li>Echoed: Pendant une minute vous pouvez ajouter <u>1d4</u> à tout vos jet de compétence.</li>
    </ul>
  </li>
  <li><strong>La Guerre est un Langage.</strong>
 En passant une minute à interagir pacifiquement avec une creature qui a au moins un langage vous pouvez ensuite utiliser des sons et des gestes pour communiquer avec cette créature comme si vous maitrisiez dans la langue de cette créature. <u></u>
    <ul>
      <li>Echoed: En passant une minute à interagir pacifiquement avec une creature qui a au moins un langage vous pouvez ensuite utiliser des sons et des gestes pour communiquer avec cette créature comme si vous maitrisiez dans la langue de cette créature. <u>Cela fonctionne tant que ce que vous dites n'est pas trop compliqué (à la discrétion du MJ).</u></li>
    </ul>
  </li>
  <li><strong>Escroquer avec Confiance.</strong>
(inspired by <a href="https://drive.google.com/file/d/0B40nce9YZ1MbTDJsVjh0ZFpXenc/view">Thirteen Expanded Feats</a>/)
 Si vous réussissez à un jet de Charisme (Tromperie) contre une créature, le prochaine jet de Charisme (Tromperie) que vous faites à un bonus de <u>1d8</u> s’il est fait dans les 10 minutes qui suivent le prmier jet.
    <ul>
      <li>Echoed: Si vous réussissez à un jet de Charisme (Tromperie) contre une créature, le prochaine jet de Charisme (Tromperie) que vous faites à un bonus de <u>1d4</u> s’il est fait dans les 10 minutes qui suivent le prmier jet.</li>
    </ul>
  </li>
  <li><strong>Vous Pouvez Toujours Courrir …</strong>
 En vous concentrant sur vos sens vous gagnez une vision aveugle (15 pieds) pendant <u>10</u> minutes.
    <ul>
      <li>Echoed: En vous concentrant sur vos sens vous gagnez une vision aveugle (15 pieds) pendant <u>1</u> minutes.</li>
    </ul>
  </li>
  <li><strong>Ordre Martial.</strong>
(inspired by <a href="https://dnd.wizards.com/articles/features/basicrules">Player’s Handbook</a>/Command spell)
 Vous donnez un ordre d’un mot à une créature à moins 60 pieds ou moins et que vous pouvez voir. La cible doit <u></u> suivre l’ordre lors de son prochain tour. Cet ordre n’a aucun effet si la cible ne comprend pas votre langue, ou si votre ordre est directement nocif pour elle.
    <ul>
      <li>Echoed: Vous donnez un ordre d’un mot à une créature à moins 60 pieds ou moins et que vous pouvez voir. La cible doit <u>succeed on a Wisdom saving throw or</u> suivre l’ordre lors de son prochain tour. Cet ordre n’a aucun effet si la cible ne comprend pas votre langue, ou si votre ordre est directement nocif pour elle.</li>
    </ul>
  </li>
  <li><strong>Choisis tes Combats.</strong>
 Vous essayez de calmer un groupe de personne. <u>Choisissez l'un des deux effets suivants</u>. Vous pouvez annuler tout effet faisant qu’une créature est effrayé ou charmée. Alternativement, vous pouvez faire qu’une cible devienne indiférente à une créature de votre choix. Les effets durent tant que vous continuer à converser avec les cibles pour une durée maximume de <u>une heure</u>.
    <ul>
      <li>Echoed: Vous essayez de calmer un groupe de personne. <u>Chaque créature qui peut vous entendre et vous comprendre doit faire un jet de sauvegarde de Charisme; une créature peut choisir d'échouer à ce jet de sauvegarde si elle le souhaite. Si une créature échoue à son jet de sauvegarde choisissez l'un des deux effets suivants</u>. Vous pouvez annuler tout effet faisant qu’une créature est effrayé ou charmée. Alternativement, vous pouvez faire qu’une cible devienne indiférente à une créature de votre choix. Les effets durent tant que vous continuer à converser avec les cibles pour une durée maximume de <u>une minute</u>.</li>
    </ul>
  </li>
</ol>

<h3 id="tier-2-12">Tier 2 (12)</h3>

<h4 id="actions-héroïques-tactique-6">Actions Héroïques Tactique (6)</h4>

<ol>
  <li><strong>Je ne m’inclinerai pas.</strong>
 <u>Au lieu de faire</u> un jet de sauvegarde contre la mort vous regagner 1 point de vie. Vous pouvez ensuite agir normalement.
    <ul>
      <li>Echoed: <u>Si vous réussissez</u> un jet de sauvegarde contre la mort vous regagner 1 point de vie. Vous pouvez ensuite agir normalement.</li>
    </ul>
  </li>
  <li><strong>Encore. Et encore !</strong>
 Pendant votre tour vous pouvez réaliser <u>deux</u> action supplémentaires en plus de vos actions habituelles et  <u>deux</u> action bonus.
    <ul>
      <li>Echoed, <strong>Encore</strong>: Pendant votre tour vous pouvez réaliser <u>une</u> action supplémentaires en plus de vos actions habituelles et  <u>une</u> action bonus.</li>
    </ul>
  </li>
  <li><strong>Consient.</strong>
 Pendant la prochaine <u>hour</u> vous ne pouvez pas être surpris et avez l’avantage au jet d’attaque avec une armes, jets de compétence, et jets de sauvegarde. De plus, les autres créatures on un désavantage aux jet d’attaque avec une armes contre vous pendant cette même durée.
    <ul>
      <li>Echoed: Pendant la prochaine <u>minute</u> vous ne pouvez pas être surpris et avez l’avantage au jet d’attaque avec une armes, jets de compétence, et jets de sauvegarde. De plus, les autres créatures on un désavantage aux jet d’attaque avec une armes contre vous pendant cette même durée.</li>
    </ul>
  </li>
  <li><strong>Non.</strong>
 Contrecarrer un sort si le lanceur est à portée de votre arme.
    <ul>
      <li>Echoed: Contrecarrer un sort si le lanceur est à portée de votre arme <u>et si le lanceur échoue à un jet de sauvegarde contre votre DD d'arme</u>.</li>
    </ul>
  </li>
  <li><strong>Dans la Tête.</strong>
 Votre prochaine attaque est un coup critique.
    <ul>
      <li>Echoed: Votre prochaine attaque est un coup critique <u>sur un 12 ou plus</u>.</li>
    </ul>
  </li>
  <li><strong>Regarde Moi dans les Yeux.</strong>
 Chaque créatire dans un cône de 30 pieds <u></u> lâche tout ce qu’elle tient et devient effrayée pendant 3 tours. Tant qu’elle est effrayée par cet effet une créature doit utiliser l’action Foncer pour s’éloigner de vous à chacun de ses tours. <u></u>
    <ul>
      <li>Echoed: Chaque créatire dans un cône de 30 pieds <u>must succeed on a Wisdom saving throw or</u> lâche tout ce qu’elle tient et devient effrayée pendant 3 tours. Tant qu’elle est effrayée par cet effet une créature doit utiliser l’action Foncer pour s’éloigner de vous à chacun de ses tours. <u>À la fin de son tour, si la créature ne vous voit pas elle peut faire un jet de sauvegarde de Sagesse et mettre fin à l'effet en cas de succès.</u></li>
    </ul>
  </li>
</ol>

<h4 id="actions-héroïques-non-tactique-6">Actions Héroïques Non-tactique (6)</h4>

<ol>
  <li><strong>Paragon d’Effort.</strong>
 Quelque chose qui devrait prendre jusqu’à <u>une semaine</u> pour être fait avec des outils que vous maîtrisez peut être fait <u>7</u> fois plus vite.
    <ul>
      <li>Echoed: Quelque chose qui devrait prendre jusqu’à <u>un jour</u> pour être fait avec des outils que vous maîtrisez peut être fait <u>3</u> fois plus vite.</li>
    </ul>
  </li>
  <li><strong>Je Suis le Maître de mon Destin.</strong>
 Après avoir échoué un jet de compétence vous pouvez <u>choisir de réussir à la place</u>.
    <ul>
      <li>Echoed: Après avoir échoué un jet de compétence vous pouvez <u>lancez un d20 en plus, vous choisissez quel résultat utiliser</u>.</li>
    </ul>
  </li>
  <li><strong>Je Suis le Capitaine de mon Âme.</strong>
 Après avoir échoué a un jet de sauvegarde vous pouvez <u>choisir de réussir à la place</u>.
    <ul>
      <li>Echoed: Après avoir échoué a un jet de sauvegarde vous pouvez <u>lancez un d20 en plus, vous choisissez quel résultat utiliser</u>.</li>
    </ul>
  </li>
  <li><strong>Saute.</strong>
 Vous gagnez une vitesse de vol égale à <u>deux fois</u> votre vitesse de marche pendant <u>2 round</u>. <u>Après ces 2 rounds vous ne pouvez plus monter mais vous pouvez toujours aller tout droit ou vers le bas pour un round. ENsuite, vous tombez.</u>
    <ul>
      <li>Echoed: Vous gagnez une vitesse de vol égale à <u></u> votre vitesse de marche pendant <u>1 round</u>. <u></u></li>
    </ul>
  </li>
  <li><strong>Retour au Pays.</strong>
 Avec une démonstration de force et d’endurance impossible vous passez <u>8 heures</u> à travailler dans les champs autour de vous, réalisant un travail équivalent à celui de dizaines de personnes. Vous enrichissez le sol pour une année dans une zone de 800 mètres de rayon. Les plantes produisent deux fois plus de nourriture lors de la récolte.
    <ul>
      <li>Echoed: Avec une démonstration de force et d’endurance impossible vous passez <u>une semaine</u> à travailler dans les champs autour de vous, réalisant un travail équivalent à celui de dizaines de personnes. Vous enrichissez le sol pour une année dans une zone de 800 mètres de rayon. Les plantes produisent deux fois plus de nourriture lors de la récolte.</li>
    </ul>
  </li>
  <li><strong>Conseil de Sage</strong>
(inspired by <a href="https://www.gmbinder.com/share/-L8hC0Kqh4yvXSY_v3aB">The Scholar</a>/Sage Advice and Theoretical Advice)
 Vous pouvez transmettre vos connaissances et votre experience à ceux autour de vous. Vous passez une minute à conseiller vos compagnons. Quand vous faites ceci, choisissez une compétence ou un outils <u></u> et un nombre de créature égale à votre modificateur d’Intelligence qui peuvent vous comprendre, vous voir et vous entendre. <u>Deux fois</u> pendant la prochaine heure, quand la créature devrait faire un jet de compétence ou d’outils que vous avez choisis elle peut ajouter son bonus de maitrise au jet, ou le double si elle le maitrisait déjà.
    <ul>
      <li>Echoed: Vous pouvez transmettre vos connaissances et votre experience à ceux autour de vous. Vous passez une minute à conseiller vos compagnons. Quand vous faites ceci, choisissez une compétence ou un outils <u>you are proficient with</u> et un nombre de créature égale à votre modificateur d’Intelligence qui peuvent vous comprendre, vous voir et vous entendre. <u>Une fois</u> pendant la prochaine heure, quand la créature devrait faire un jet de compétence ou d’outils que vous avez choisis elle peut ajouter son bonus de maitrise au jet, ou le double si elle le maitrisait déjà.</li>
    </ul>
  </li>
</ol>

<h3 id="tier-3-6">Tier 3 (6)</h3>

<h4 id="actions-héroïques-non-tactique-3">Actions Héroïques Non-tactique (3)</h4>

<ol>
  <li><strong>Ne me Dit Jamais la Cote !</strong>
(inspired by <a href="https://www.dmsguild.com/product/233170/">Balsar’s Guide to Exploration</a>/Foretell the Odds)
 Quand une créature à portée de votre arme fait un jet d’attaque, un jet de compétence ou un jet de sauvegarde, vous pouvez utilisez votre réaction pour faire que leur lancer devienne <u>2 ou 18</u> (avant les modificateurs). Vous pouvez choisir de faire ceci après le lancer, mais avant que le succès ou l’echec soit annoncé.
    <ul>
      <li>Echoed: Quand une créature à portée de votre arme fait un jet d’attaque, un jet de compétence ou un jet de sauvegarde, vous pouvez utilisez votre réaction pour faire que leur lancer devienne <u>10</u> (avant les modificateurs). Vous pouvez choisir de faire ceci après le lancer, mais avant que le succès ou l’echec soit annoncé.</li>
    </ul>
  </li>
  <li><strong>Je suis en Veine.</strong>
 Vous avez l’avantage à tous vos jet de caracteristique pendant You have advantage at all you ability checks for the <u>une heure</u> ou jusqu’à ce que vous échouillez à l’un d’entre eux, le premier des deux prévalant.
    <ul>
      <li>Echoed: Vous avez l’avantage à tous vos jet de caracteristique pendant You have advantage at all you ability checks for the <u>une minute</u> ou jusqu’à ce que vous échouillez à l’un d’entre eux, le premier des deux prévalant.</li>
    </ul>
  </li>
  <li><strong>Tu Vas m’Écouter.</strong>
 Si vous ne vous comportez pas de manière agressive avec aucune des personne qui peut vous voir dans un rayon de 30 pieds <u>paraliser</u> tant que vous n’attaquez pas ou pour <u>une minute</u>, le premier des deux prévalant. <u><u>Vous avez l'avantage aux jets de Charisme que vous faites pendant cette periode.</u></u>
    <ul>
      <li>Echoed: Si vous ne vous comportez pas de manière agressive avec aucune des personne qui peut vous voir dans un rayon de 30 pieds <u>étourdi</u> tant que vous n’attaquez pas ou pour <u>10 secondes</u>, le premier des deux prévalant. <u></u></li>
    </ul>
  </li>
</ol>

<h4 id="actions-héroïques-tactique-3">Actions Héroïques Tactique (3)</h4>

<ol>
  <li><strong>Toi tu Crèves.</strong>
 Pendant ce tour toutes vos attaques <u>sont des coups critiques</u>.
    <ul>
      <li>Echoed: Pendant ce tour toutes vos attaques <u>touchent (vous pouvez still roll the dice in case of a critical hit)</u>.</li>
    </ul>
  </li>
  <li><strong>Ce n’est qu’une Égratignure.</strong>
 Vous regagnez <u>tout</u> vos points de vie.
    <ul>
      <li>Echoed: Vous regagnez <u>la moitié de</u> vos points de vie.</li>
    </ul>
  </li>
  <li><strong>Toujours Debout.</strong>
 Vous pouvez utiliser votre reaction pour <u>récuire à zéro</u> tous les dégâts que vous allez prendre pendant ce tour.
    <ul>
      <li>Echoed: Vous pouvez utiliser votre reaction pour <u>diviser par deux</u> tous les dégâts que vous allez prendre pendant ce tour.</li>
    </ul>
  </li>
</ol>

<h3 id="règles-optionnelles">Règles Optionnelles</h3>

<h4 id="les-roublard-sont-autorisés">Les Roublard Sont Autorisés</h4>

<p>Normallement les roublards ne peuvent pas utiliser les actions heroïques, avec cette règle il peuvent.</p>

<h4 id="niveau-de-héros-moyen">Niveau de Héros Moyen</h4>

<p>Si vous décidez d’utiliser cette règle optionnelle votre rang de héros est determiné par vos niveaux dans chacune de vos classes. Les classes sont réparties en 4 catégories :</p>
<ul>
  <li><strong>Lanceurs de sorts complets</strong> (magicien, druides, bardes, clercs, sorciers) qui ont un <strong>emplacement de sorts de niveau 9</strong> comme emplacement de sort de plus haut niveau</li>
  <li><strong>Demi lanceurs de sorts</strong> (paladins, rôdeurs, artificers, et pour ce qui est de ce module les occultistes) qui ont un <strong>emplacement de sorts de niveau 5</strong> comme emplacement de sort de plus haut niveau</li>
  <li><strong>Tiers lanceurs de sorts</strong> (guerrier/chevalier occultes, roublards/escroc arcanique) qui ont <strong>un emplacement de sorts de niveau 4</strong> comme emplacement de sort de plus haut niveau</li>
  <li><strong>Non lanceurs de sorts</strong> (guerrier, barbare, moine, roublard) qui n’ont pas de sorts</li>
</ul>

<p>Votre rang de héro est égale à (1/2 × la somme de vos niveau dans des classes étant de demi lanceur de sorts) + (2/3 × la somme de vos niveau dans des classes étant de tiers lanceur de sorts) + la somme de vos niveau dans des classes non lanceur de sorts.</p>

<h4 id="la-magie-gâche-tout">La Magie Gâche Tout</h4>

<p>Si vous décidez d’utiliser cette règle optionelle votre rang héroïque est déterminé par votre classe ayant le niveau de magie le plus élevé. Si vous avez uniquement des niveaux dans des classe qui ne lancent pas de sorts ou tiers lanceur de sorts alors votre rang de héro est calculé normalement. Si vous avez au moins un niveau dans une classe demi lancer de sorts alors votre rang de héro est divisé par deux. Si vous avez un niveau dans une classe qui est lanceur de sorts complet alors vous n’avez pas de rang de héro (et donc pas d’actions héroïques).</p>

<h2 id="conclusion">Conclusion</h2>

<p>Il y a beaucoup de choses dont je ne suis pas sûr. Pourquoi seulement 5 actions héroïques au début et deux de plus à chaque rang ? Peut-être que c’est trop ? Peut-être pas ?</p>

<p>J’aime que la joueuse soit encouragée à utiliser les actions parce que sinon la version écho et en quelques sortes perdue mais en même temps elle est aussi encouragée à la garder pour un moment très important.</p>

<p>Je ne suis pas certain de la division en tiers. Peut-être que je devrais tout mettre dans le même tier ? Peut-être que les actions de tier 2 sont meilleurs que celles du tier 3 ?</p>

<p>Aussi, peut-être que les écho devraient être plus fort que les version normales ? Ça inciterait plus à les utiliser.</p>

<p>Il y a probablement des fautes dans l’article, laissez un commentaire et je corrigerai.</p>

<p>Peut-être qu’un jour je mettrai à jour cette article avec de nouvelles actions.</p>]]></content><author><name></name></author><category term="dnd" /><category term="dnd5" /><category term="ttrpg" /><category term="fr" /><summary type="html"><![CDATA[Il y a quelques mois Dael Kingsmill a publié une video essayant de donner un peu plus d’intérêt au champion. Ça concerne plutôt l’entiéreté de la classe Guerrier à mon avis. J’aime bien ce qu’elle a proposé et ça m’a rappelé que j’avais pour projet d’essayer de rendre les classes martiales plus intéressantes. En parlant du guerrier Dael dit “J’espère que, enfin, cette fois, ils seront ce qu’il doivent être”. C’est quelque chose auquel je suis sensible et que je ressent depuis assez longtemps, c’est vrai pour les guerrier mais aussi des autres classe martiales : elle sont très monotones en dehors des combats. De toute évidente c’est mon opinion, et si vous n’êtes pas d’accord vous allez trouver tout ceci très inintéressant.]]></summary></entry><entry xml:lang="en"><title type="html">Safe and Strict bind and apply Functions in Typescript</title><link href="https://playest.net/blog/2020/10/10/Safe-and-strict-bind-and-apply-functions-in-Typescript.html" rel="alternate" type="text/html" title="Safe and Strict bind and apply Functions in Typescript" /><published>2020-10-10T00:00:00+02:00</published><updated>2020-10-10T00:00:00+02:00</updated><id>https://playest.net/blog/2020/10/10/Safe%20and%20strict%20bind%20and%20apply%20functions%20in%20Typescript</id><content type="html" xml:base="https://playest.net/blog/2020/10/10/Safe-and-strict-bind-and-apply-functions-in-Typescript.html"><![CDATA[<p>You may know the functions <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind">bind</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply">apply</a> (and <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call">call</a>, but it’s basically the same as apply and I don’t use it anyway) from javascript.</p>

<p>The idea behind those function is that for an object <code class="language-plaintext highlighter-rouge">o</code> with a method <code class="language-plaintext highlighter-rouge">f</code> we have <code class="language-plaintext highlighter-rouge">o.f(a)</code> equivalent to <code class="language-plaintext highlighter-rouge">o.f.apply(o, [a])</code> or <code class="language-plaintext highlighter-rouge">o.f.bind(o)(a)</code>.</p>

<p>The problem is that their respective typings made for Typescript are too permissive for my own taste. They allow too much.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">Plane</span> <span class="p">{</span>
    <span class="nx">flyTo</span><span class="p">(</span><span class="nx">lat</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">lon</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="p">{</span>
        <span class="k">return</span> <span class="dl">"</span><span class="s2">flying to </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">lat</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">, </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">lon</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">let</span> <span class="nx">o</span><span class="p">:</span> <span class="nb">Object</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Object</span><span class="p">();</span>
<span class="kd">let</span> <span class="nx">p</span><span class="p">:</span> <span class="nx">Plane</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Plane</span><span class="p">();</span>

<span class="nx">p</span><span class="p">.</span><span class="nx">flyTo</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="nx">p</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]);</span>
<span class="nx">p</span><span class="p">.</span><span class="nx">flyTo</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="nx">o</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]);</span> <span class="c1">// I don't want that to typecheck! o is not a plane.</span>
</code></pre></div></div>

<p>For reference here are the typings for those functions in the typescript library (you can skip them if you want):</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">interface</span> <span class="nb">Function</span> <span class="p">{</span>
    <span class="nx">apply</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nb">Function</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="nx">argArray</span><span class="p">?:</span> <span class="kr">any</span><span class="p">):</span> <span class="kr">any</span><span class="p">;</span>

    <span class="nx">call</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nb">Function</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="p">...</span><span class="nx">argArray</span><span class="p">:</span> <span class="kr">any</span><span class="p">[]):</span> <span class="kr">any</span><span class="p">;</span>

    <span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nb">Function</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="p">...</span><span class="nx">argArray</span><span class="p">:</span> <span class="kr">any</span><span class="p">[]):</span> <span class="kr">any</span><span class="p">;</span>

    <span class="c1">// ...</span>
<span class="p">}</span>

<span class="kr">interface</span> <span class="nx">CallableFunction</span> <span class="kd">extends</span> <span class="nb">Function</span> <span class="p">{</span>
    <span class="nx">apply</span><span class="o">&lt;</span><span class="nx">T</span><span class="p">,</span> <span class="nx">R</span><span class="o">&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nx">T</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="nx">T</span><span class="p">):</span> <span class="nx">R</span><span class="p">;</span>
    <span class="nx">apply</span><span class="o">&lt;</span><span class="nx">T</span><span class="p">,</span> <span class="nx">A</span> <span class="kd">extends</span> <span class="kr">any</span><span class="p">[],</span> <span class="nx">R</span><span class="o">&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">):</span> <span class="nx">R</span><span class="p">;</span>

    <span class="nx">call</span><span class="o">&lt;</span><span class="nx">T</span><span class="p">,</span> <span class="nx">A</span> <span class="kd">extends</span> <span class="kr">any</span><span class="p">[],</span> <span class="nx">R</span><span class="o">&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">):</span> <span class="nx">R</span><span class="p">;</span>

    <span class="nx">bind</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="nx">ThisParameterType</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">):</span> <span class="nx">OmitThisParameter</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="nx">bind</span><span class="o">&lt;</span><span class="nx">T</span><span class="p">,</span> <span class="nx">A0</span><span class="p">,</span> <span class="nx">A</span> <span class="kd">extends</span> <span class="kr">any</span><span class="p">[],</span> <span class="nx">R</span><span class="o">&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">arg0</span><span class="p">:</span> <span class="nx">A0</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">arg0</span><span class="p">:</span> <span class="nx">A0</span><span class="p">):</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">;</span>
    <span class="nx">bind</span><span class="o">&lt;</span><span class="nx">T</span><span class="p">,</span> <span class="nx">A0</span><span class="p">,</span> <span class="nx">A1</span><span class="p">,</span> <span class="nx">A</span> <span class="kd">extends</span> <span class="kr">any</span><span class="p">[],</span> <span class="nx">R</span><span class="o">&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">arg0</span><span class="p">:</span> <span class="nx">A0</span><span class="p">,</span> <span class="nx">arg1</span><span class="p">:</span> <span class="nx">A1</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">arg0</span><span class="p">:</span> <span class="nx">A0</span><span class="p">,</span> <span class="nx">arg1</span><span class="p">:</span> <span class="nx">A1</span><span class="p">):</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">;</span>
    <span class="nx">bind</span><span class="o">&lt;</span><span class="nx">T</span><span class="p">,</span> <span class="nx">A0</span><span class="p">,</span> <span class="nx">A1</span><span class="p">,</span> <span class="nx">A2</span><span class="p">,</span> <span class="nx">A</span> <span class="kd">extends</span> <span class="kr">any</span><span class="p">[],</span> <span class="nx">R</span><span class="o">&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">arg0</span><span class="p">:</span> <span class="nx">A0</span><span class="p">,</span> <span class="nx">arg1</span><span class="p">:</span> <span class="nx">A1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">:</span> <span class="nx">A2</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">arg0</span><span class="p">:</span> <span class="nx">A0</span><span class="p">,</span> <span class="nx">arg1</span><span class="p">:</span> <span class="nx">A1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">:</span> <span class="nx">A2</span><span class="p">):</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">;</span>
    <span class="nx">bind</span><span class="o">&lt;</span><span class="nx">T</span><span class="p">,</span> <span class="nx">A0</span><span class="p">,</span> <span class="nx">A1</span><span class="p">,</span> <span class="nx">A2</span><span class="p">,</span> <span class="nx">A3</span><span class="p">,</span> <span class="nx">A</span> <span class="kd">extends</span> <span class="kr">any</span><span class="p">[],</span> <span class="nx">R</span><span class="o">&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">arg0</span><span class="p">:</span> <span class="nx">A0</span><span class="p">,</span> <span class="nx">arg1</span><span class="p">:</span> <span class="nx">A1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">:</span> <span class="nx">A2</span><span class="p">,</span> <span class="nx">arg3</span><span class="p">:</span> <span class="nx">A3</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">arg0</span><span class="p">:</span> <span class="nx">A0</span><span class="p">,</span> <span class="nx">arg1</span><span class="p">:</span> <span class="nx">A1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">:</span> <span class="nx">A2</span><span class="p">,</span> <span class="nx">arg3</span><span class="p">:</span> <span class="nx">A3</span><span class="p">):</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">;</span>
    <span class="nx">bind</span><span class="o">&lt;</span><span class="nx">T</span><span class="p">,</span> <span class="nx">AX</span><span class="p">,</span> <span class="nx">R</span><span class="o">&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">AX</span><span class="p">[])</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">AX</span><span class="p">[]):</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">AX</span><span class="p">[])</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">;</span>
<span class="p">}</span>

<span class="kr">interface</span> <span class="nx">NewableFunction</span> <span class="kd">extends</span> <span class="nb">Function</span> <span class="p">{</span>
    <span class="nx">apply</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="k">new</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="nx">T</span><span class="p">):</span> <span class="k">void</span><span class="p">;</span>
    <span class="nx">apply</span><span class="o">&lt;</span><span class="nx">T</span><span class="p">,</span> <span class="nx">A</span> <span class="kd">extends</span> <span class="kr">any</span><span class="p">[]</span><span class="o">&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="k">new</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">):</span> <span class="k">void</span><span class="p">;</span>

    <span class="nx">call</span><span class="o">&lt;</span><span class="nx">T</span><span class="p">,</span> <span class="nx">A</span> <span class="kd">extends</span> <span class="kr">any</span><span class="p">[]</span><span class="o">&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="k">new</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">):</span> <span class="k">void</span><span class="p">;</span>

    <span class="nx">bind</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="kr">any</span><span class="p">):</span> <span class="nx">T</span><span class="p">;</span>
    <span class="nx">bind</span><span class="o">&lt;</span><span class="nx">A0</span><span class="p">,</span> <span class="nx">A</span> <span class="kd">extends</span> <span class="kr">any</span><span class="p">[],</span> <span class="nx">R</span><span class="o">&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="k">new</span> <span class="p">(</span><span class="nx">arg0</span><span class="p">:</span> <span class="nx">A0</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="nx">arg0</span><span class="p">:</span> <span class="nx">A0</span><span class="p">):</span> <span class="k">new</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">;</span>
    <span class="nx">bind</span><span class="o">&lt;</span><span class="nx">A0</span><span class="p">,</span> <span class="nx">A1</span><span class="p">,</span> <span class="nx">A</span> <span class="kd">extends</span> <span class="kr">any</span><span class="p">[],</span> <span class="nx">R</span><span class="o">&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="k">new</span> <span class="p">(</span><span class="nx">arg0</span><span class="p">:</span> <span class="nx">A0</span><span class="p">,</span> <span class="nx">arg1</span><span class="p">:</span> <span class="nx">A1</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="nx">arg0</span><span class="p">:</span> <span class="nx">A0</span><span class="p">,</span> <span class="nx">arg1</span><span class="p">:</span> <span class="nx">A1</span><span class="p">):</span> <span class="k">new</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">;</span>
    <span class="nx">bind</span><span class="o">&lt;</span><span class="nx">A0</span><span class="p">,</span> <span class="nx">A1</span><span class="p">,</span> <span class="nx">A2</span><span class="p">,</span> <span class="nx">A</span> <span class="kd">extends</span> <span class="kr">any</span><span class="p">[],</span> <span class="nx">R</span><span class="o">&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="k">new</span> <span class="p">(</span><span class="nx">arg0</span><span class="p">:</span> <span class="nx">A0</span><span class="p">,</span> <span class="nx">arg1</span><span class="p">:</span> <span class="nx">A1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">:</span> <span class="nx">A2</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="nx">arg0</span><span class="p">:</span> <span class="nx">A0</span><span class="p">,</span> <span class="nx">arg1</span><span class="p">:</span> <span class="nx">A1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">:</span> <span class="nx">A2</span><span class="p">):</span> <span class="k">new</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">;</span>
    <span class="nx">bind</span><span class="o">&lt;</span><span class="nx">A0</span><span class="p">,</span> <span class="nx">A1</span><span class="p">,</span> <span class="nx">A2</span><span class="p">,</span> <span class="nx">A3</span><span class="p">,</span> <span class="nx">A</span> <span class="kd">extends</span> <span class="kr">any</span><span class="p">[],</span> <span class="nx">R</span><span class="o">&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="k">new</span> <span class="p">(</span><span class="nx">arg0</span><span class="p">:</span> <span class="nx">A0</span><span class="p">,</span> <span class="nx">arg1</span><span class="p">:</span> <span class="nx">A1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">:</span> <span class="nx">A2</span><span class="p">,</span> <span class="nx">arg3</span><span class="p">:</span> <span class="nx">A3</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="nx">arg0</span><span class="p">:</span> <span class="nx">A0</span><span class="p">,</span> <span class="nx">arg1</span><span class="p">:</span> <span class="nx">A1</span><span class="p">,</span> <span class="nx">arg2</span><span class="p">:</span> <span class="nx">A2</span><span class="p">,</span> <span class="nx">arg3</span><span class="p">:</span> <span class="nx">A3</span><span class="p">):</span> <span class="k">new</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">A</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">;</span>
    <span class="nx">bind</span><span class="o">&lt;</span><span class="nx">AX</span><span class="p">,</span> <span class="nx">R</span><span class="o">&gt;</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="k">new</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">AX</span><span class="p">[])</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">AX</span><span class="p">[]):</span> <span class="k">new</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">AX</span><span class="p">[])</span> <span class="o">=&gt;</span> <span class="nx">R</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I wanted to add my own definitions to the typescript standard library but I’m not a fan of editing a code that could be erased with an update so I made wrappers. The first iteration was:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">safeApply</span><span class="o">&lt;</span><span class="nx">T</span> <span class="kd">extends</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="kr">any</span><span class="o">&gt;</span><span class="p">(</span><span class="nx">fun</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="nx">ThisParameterType</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">,</span> <span class="nx">args</span><span class="p">:</span> <span class="nx">Parameters</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">):</span> <span class="nx">ReturnType</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">fun</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="nx">thisArg</span><span class="p">,</span> <span class="nx">args</span><span class="p">);</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nx">safeBind</span><span class="o">&lt;</span><span class="nx">T</span> <span class="kd">extends</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="kr">any</span><span class="o">&gt;</span><span class="p">(</span><span class="nx">fun</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="nx">ThisParameterType</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">):</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">Parameters</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">ReturnType</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">{</span>
    <span class="k">return</span> <span class="nx">fun</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="nx">thisArg</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>For those who don’t know ThisParameterType, Parameters and ReturnType all comes from the <a href="https://www.typescriptlang.org/docs/handbook/utility-types.html">utility types provided by typescript</a>:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/**
 * Obtain the parameters of a function type in a tuple
 */</span>
<span class="kd">type</span> <span class="nx">Parameters</span><span class="o">&lt;</span><span class="nx">T</span> <span class="kd">extends</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="kr">any</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nx">T</span> <span class="kd">extends</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">infer</span> <span class="nx">P</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="kr">any</span> <span class="p">?</span> <span class="nx">P</span> <span class="p">:</span> <span class="nx">never</span><span class="p">;</span>

<span class="cm">/**
 * Obtain the return type of a function type
 */</span>
<span class="kd">type</span> <span class="nx">ReturnType</span><span class="o">&lt;</span><span class="nx">T</span> <span class="kd">extends</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="kr">any</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nx">T</span> <span class="kd">extends</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">infer</span> <span class="nx">R</span> <span class="p">?</span> <span class="nx">R</span> <span class="p">:</span> <span class="kr">any</span><span class="p">;</span>

<span class="cm">/**
 * Extracts the type of the 'this' parameter of a function type, or 'unknown' if the function type has no 'this' parameter.
 */</span>
<span class="kd">type</span> <span class="nx">ThisParameterType</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nx">T</span> <span class="kd">extends</span> <span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nx">infer</span> <span class="nx">U</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="kr">any</span><span class="p">[])</span> <span class="o">=&gt;</span> <span class="kr">any</span> <span class="p">?</span> <span class="nx">U</span> <span class="p">:</span> <span class="nx">unknown</span><span class="p">;</span>
</code></pre></div></div>

<p>The syntax is weird, it uses <a href="https://www.typescriptlang.org/docs/handbook/advanced-types.html#conditional-types">conditional-types</a> which are conditions evaluated at type checking time. The doc says:</p>

<blockquote>
  <p>A conditional type selects one of two possible types based on a condition expressed as a type relationship test:</p>

  <p>T extends U ? X : Y</p>

  <p>The type above means when T is assignable to U the type is X, otherwise the type is Y.</p>
</blockquote>

<p>So <code class="language-plaintext highlighter-rouge">type Parameters&lt;T extends (...args: any) =&gt; any&gt; = T extends (...args: infer P) =&gt; any ? P : never;</code> would read something like
The type <code class="language-plaintext highlighter-rouge">Parameter&lt;T&gt;</code>, which is only valid if T is a function (<code class="language-plaintext highlighter-rouge">(...args: any) =&gt; any</code> is a function), is either:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">P</code> if T is a function with the list of arguments is of type P</li>
  <li><code class="language-plaintext highlighter-rouge">never</code> otherwise</li>
</ul>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Basically this means that all of this is correct</span>
<span class="kd">let</span> <span class="nx">par</span><span class="p">:</span> <span class="nx">Parameters</span><span class="o">&lt;</span><span class="nx">Plane</span><span class="p">[</span><span class="dl">"</span><span class="s2">flyTo</span><span class="dl">"</span><span class="p">]</span><span class="o">&gt;</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">];</span> <span class="c1">// because the arguments of the flyTo method are [number, number]</span>
<span class="kd">let</span> <span class="nx">ret</span><span class="p">:</span> <span class="nx">ReturnType</span><span class="o">&lt;</span><span class="nx">Plane</span><span class="p">[</span><span class="dl">"</span><span class="s2">flyTo</span><span class="dl">"</span><span class="p">]</span><span class="o">&gt;</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">some string</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// because flyTo returns a string</span>
<span class="kd">let</span> <span class="nx">thi</span><span class="p">:</span> <span class="nx">ThisParameterType</span><span class="o">&lt;</span><span class="nx">Plane</span><span class="p">[</span><span class="dl">"</span><span class="s2">flyTo</span><span class="dl">"</span><span class="p">]</span><span class="o">&gt;</span> <span class="o">=</span> <span class="kc">undefined</span><span class="p">;</span> <span class="c1">// because ... well, I don't really know, it should be Plane, we are going to fix that</span>
</code></pre></div></div>

<p>Why does <code class="language-plaintext highlighter-rouge">ThisParameterType&lt;Plane["flyTo"]&gt;</code> evaluates to <code class="language-plaintext highlighter-rouge">undefined</code>? Apparently it’s because typescript need to be said what is the type of <code class="language-plaintext highlighter-rouge">this</code> in the function. Like that:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">Plane</span> <span class="p">{</span>
    <span class="nx">flyTo</span><span class="p">(</span><span class="nx">lat</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">lon</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="p">{</span>
        <span class="k">return</span> <span class="dl">"</span><span class="s2">flying to </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">lat</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">, </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">lon</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="nx">flyToWithThis</span><span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nx">Plane</span><span class="p">,</span> <span class="nx">lat</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">lon</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">string</span> <span class="p">{</span>
        <span class="k">return</span> <span class="dl">"</span><span class="s2">flying to </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">lat</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">, </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">lon</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">o</span><span class="p">:</span> <span class="nb">Object</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Object</span><span class="p">();</span>
    <span class="kd">let</span> <span class="nx">p</span><span class="p">:</span> <span class="nx">Plane</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Plane</span><span class="p">();</span>

    <span class="nx">safeApply</span><span class="p">(</span><span class="nx">p</span><span class="p">.</span><span class="nx">flyToWithThis</span><span class="p">,</span> <span class="nx">p</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]);</span> <span class="c1">// this works, so far so good</span>
    <span class="nx">safeApply</span><span class="p">(</span><span class="nx">p</span><span class="p">.</span><span class="nx">flyToWithThis</span><span class="p">,</span> <span class="nx">o</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]);</span> <span class="c1">// and this gives an error! Finally!</span>
    <span class="cm">/*
    Argument of type 'Object' is not assignable to parameter of type 'Plane'.
        The 'Object' type is assignable to very few other types. Did you mean to use the 'any' type instead?
            Type 'Object' is missing the following properties from type 'Plane': flyTo, flyToWithThists(2345)
    */</span>

    <span class="kd">let</span> <span class="nx">thi</span><span class="p">:</span> <span class="nx">ThisParameterType</span><span class="o">&lt;</span><span class="nx">Plane</span><span class="p">[</span><span class="dl">"</span><span class="s2">flyToWithThis</span><span class="dl">"</span><span class="p">]</span><span class="o">&gt;</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Plane</span><span class="p">();</span> <span class="c1">// no surprise here</span>
</code></pre></div></div>

<p>Ok, so I have to put the <code class="language-plaintext highlighter-rouge">this: type</code> thingy in every function that I plan to call apply (or bind) on. Ok, I’ll do it. But what if I forgot? What if, at one point, I call bind and forgot to put the <code class="language-plaintext highlighter-rouge">this: type</code> thingy in here, it would be nice if typescript would give me a warning. Because <code class="language-plaintext highlighter-rouge">safeApply(p.flyTo, o, [1, 2]);</code> still typecheck.</p>

<p>The problem is that ThisParameterType returns <code class="language-plaintext highlighter-rouge">unknown</code> when there is no <code class="language-plaintext highlighter-rouge">this: type</code> thingy, and an <code class="language-plaintext highlighter-rouge">unknown</code> parameter can be assigned anything (which is completly normal), so let’s change that:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">type</span>     <span class="nx">ThisParameterType</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nx">T</span> <span class="kd">extends</span> <span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nx">infer</span> <span class="nx">U</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="kr">any</span><span class="p">[])</span> <span class="o">=&gt;</span> <span class="kr">any</span> <span class="p">?</span> <span class="nx">U</span>                                                             <span class="p">:</span> <span class="nx">unknown</span><span class="p">;</span>
<span class="kd">type</span> <span class="nx">SafeThisParameterType</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nx">T</span> <span class="kd">extends</span> <span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nx">unknown</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="kr">any</span><span class="p">[])</span> <span class="o">=&gt;</span> <span class="kr">any</span> <span class="p">?</span> <span class="nx">never</span> <span class="p">:</span> <span class="p">(</span><span class="nx">T</span> <span class="kd">extends</span> <span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nx">infer</span> <span class="nx">U</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">:</span> <span class="kr">any</span><span class="p">[])</span> <span class="o">=&gt;</span> <span class="kr">any</span> <span class="p">?</span> <span class="nx">U</span> <span class="p">:</span> <span class="nx">never</span> <span class="p">);</span>
</code></pre></div></div>

<p>So <code class="language-plaintext highlighter-rouge">SafeThisParameterType&lt;T&gt;</code> is <code class="language-plaintext highlighter-rouge">never</code> unless T has a <code class="language-plaintext highlighter-rouge">this: type</code> thingy, in which case it is the type of the list of arguments taken by T (T is still a function).</p>

<p>And <code class="language-plaintext highlighter-rouge">safeApply</code> and <code class="language-plaintext highlighter-rouge">safeBind</code> become:</p>
<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">safeApply</span><span class="o">&lt;</span><span class="nx">T</span> <span class="kd">extends</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="kr">any</span><span class="o">&gt;</span><span class="p">(</span><span class="nx">fun</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="nx">SafeThisParameterType</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">,</span> <span class="nx">args</span><span class="p">:</span> <span class="nx">Parameters</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">):</span> <span class="nx">ReturnType</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">fun</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="nx">thisArg</span><span class="p">,</span> <span class="nx">args</span><span class="p">);</span>
<span class="p">}</span>

<span class="kd">function</span> <span class="nx">safeBind</span><span class="o">&lt;</span><span class="nx">T</span> <span class="kd">extends</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="kr">any</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="kr">any</span><span class="o">&gt;</span><span class="p">(</span><span class="nx">fun</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span> <span class="nx">thisArg</span><span class="p">:</span> <span class="nx">SafeThisParameterType</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">):</span> <span class="p">(...</span><span class="nx">args</span><span class="p">:</span> <span class="nx">Parameters</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">ReturnType</span><span class="o">&lt;</span><span class="nx">T</span><span class="o">&gt;</span><span class="p">{</span>
    <span class="k">return</span> <span class="nx">fun</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="nx">thisArg</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    <span class="kd">let</span> <span class="nx">o</span><span class="p">:</span> <span class="nb">Object</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Object</span><span class="p">();</span>
    <span class="kd">let</span> <span class="nx">p</span><span class="p">:</span> <span class="nx">Plane</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Plane</span><span class="p">();</span>

    <span class="nx">safeApply</span><span class="p">(</span><span class="nx">p</span><span class="p">.</span><span class="nx">flyToWithThis</span><span class="p">,</span> <span class="nx">p</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]);</span> <span class="c1">// still works, good</span>
    <span class="nx">safeApply</span><span class="p">(</span><span class="nx">p</span><span class="p">.</span><span class="nx">flyToWithThis</span><span class="p">,</span> <span class="nx">o</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]);</span> <span class="c1">// still an error, still good</span>
    <span class="cm">/*
    Argument of type 'Object' is not assignable to parameter of type 'Plane'.
        The 'Object' type is assignable to very few other types. Did you mean to use the 'any' type instead?
            Type 'Object' is missing the following properties from type 'Plane': flyTo, flyToWithThists(2345)
    */</span>

    <span class="nx">safeApply</span><span class="p">(</span><span class="nx">p</span><span class="p">.</span><span class="nx">flyTo</span><span class="p">,</span> <span class="nx">p</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]);</span> <span class="c1">// gives an error! Yay! I won't forgot to add this: type</span>
    <span class="c1">// Argument of type 'Plane' is not assignable to parameter of type 'never'.ts(2345)</span>
</code></pre></div></div>

<p>All is good.</p>

<p>Obviously there is weird cases where it would make sense to use bind/call in a situation where safeBind/safeCall gives an error, in which case you are free to use them if you know what you’re doing. I often don’t.</p>]]></content><author><name></name></author><category term="programming" /><category term="javascript" /><category term="typescript" /><category term="en" /><summary type="html"><![CDATA[You may know the functions bind and apply (and call, but it’s basically the same as apply and I don’t use it anyway) from javascript.]]></summary></entry><entry xml:lang="en"><title type="html">Multiple Kinds of Success</title><link href="https://playest.net/blog/2020/09/18/Multiple-Kinds-of-Success.html" rel="alternate" type="text/html" title="Multiple Kinds of Success" /><published>2020-09-18T00:00:00+02:00</published><updated>2020-09-18T00:00:00+02:00</updated><id>https://playest.net/blog/2020/09/18/Multiple%20Kinds%20of%20Success</id><content type="html" xml:base="https://playest.net/blog/2020/09/18/Multiple-Kinds-of-Success.html"><![CDATA[<p>I found this thread of reddit called <a href="https://www.reddit.com/r/DMAcademy/comments/i6427u/psa_dms_dont_punish_success/">DMs, don’t punish success</a>.</p>

<blockquote>
  <p>My party was in a fight; I was low and I wanted to try to intimidate the enemy to buy some time. My DM flat out told me, “You need to roll a 25 or higher.” My Intimidation bonus was +6, so I had to roll a 19 or 20. I succeeded! Great right?
Guess what happened next. The enemy magic missiled me out of ‘fear’. I went down. One thing lead to another and we TPK’d. Fuck. My DM later told me that if I failed the enemy would’ve ignored me and attacked another party member who had more HP.</p>
</blockquote>

<p>The results to this action mostly revolve around:</p>
<ol>
  <li>The enemy is intimidated and either
    <ol>
      <li>decides to attack the character because they are now the main menace</li>
      <li>or decides to attack someone else because …fear!</li>
    </ol>
  </li>
  <li>The enemy is not intimidated and decides to continue as if nothing happened</li>
</ol>

<p>2 is clearly a failure. 1.1 is a success if you wanted to (a) draw the aggro, a failure if (b) you wanted anything else (like making the enemy lose a turn or retreat). 1.2 is a success if you wanted to (a) avoid getting hit, (b) a failure otherwise.</p>

<p><strong>Intent</strong> matters. The player succeeded at their roll and didn’t want to draw aggro and the DM decided that since 1.1.a could be considered a success in certain situations, it could be the result of <strong>any</strong> successful roll.</p>

<p>That is true, it could. Gotcha moments can be fun. Adversarial relationship with the DM can be fun.</p>

<p>But in my opinion, surprising the players with a different kind of adjucation can cause problems. Since the players are not able to reasonably predict the results of their action they might be tempted to do only simple actions. And then we will have players only doing thing like attacking in combat, never jumping over a fence, never trying to use a spell in an original way, because they’ll know that the risks outweigh the potential reward.</p>

<p>Personally, I try to be open about the potential results of an action, a simple “you don’t really know what’s gonna happen if you do that” often suffice.</p>

<p>Of course those is just my opinion, so do what you want.</p>]]></content><author><name></name></author><category term="dnd" /><category term="dnd5" /><category term="ttrpg" /><category term="en" /><summary type="html"><![CDATA[I found this thread of reddit called DMs, don’t punish success.]]></summary></entry><entry xml:lang="en"><title type="html">Switching Weapon</title><link href="https://playest.net/blog/2020/08/26/Switching-Weapon.html" rel="alternate" type="text/html" title="Switching Weapon" /><published>2020-08-26T00:00:00+02:00</published><updated>2020-08-26T00:00:00+02:00</updated><id>https://playest.net/blog/2020/08/26/Switching%20Weapon</id><content type="html" xml:base="https://playest.net/blog/2020/08/26/Switching-Weapon.html"><![CDATA[<p>I introduced a simple rule in my D&amp;D5 campaign about switching weapons but first I’m going to explain how all of this is supposed to work RAW (rules as written).</p>

<h1 id="how-it-works-raw">How it works RAW</h1>

<ul>
  <li>Dropping something you are holding is a <em>free action</em></li>
  <li>You have one <em>interaction</em> per turn such as drawing a weapon</li>
</ul>

<p>Which means that if you’re holding nothing in your hand you can use your interact action to draw a weapon. If you need to draw an other weapon in your other hand (or interact with an other object) you need to use you action to do so, that’s one of the perks of the <a href="https://www.dndbeyond.com/marketplace/sourcebooks/players-handbook">Dual Wielder</a> feat.</p>

<p>What about switching weapons (meaning that you are already holding one)? Let’s say you are using a short sword and that you want to draw your bow. You may do it one of two ways:</p>
<ol>
  <li>Sheathe your shortsword weapon with your “interaction”</li>
  <li>Draw your bow with your action, you have to use your action because you already used your one interaction
Or:</li>
  <li>Drop your sword of the ground (free action)</li>
  <li>Draw your bow with your one interaction</li>
  <li>Fire!</li>
</ol>

<p>An <em>interaction</em> is defined at the page 190 of the PHB:</p>
<blockquote>
  <p>You can also <strong>interact</strong> with one object or feature of the environment for free, during either your m ove or your action. For example, you could open a door during your move as you stride toward a foe, or you could draw your weapon as part of the same action you use to attack.</p>
</blockquote>

<p>If you really don’t want do drop your weapon the first one is better but it’s infrequent enough so that it doesn’t really matter, so in general the second way is better. But I think it’s stupid so let’s make a rule to make it better!</p>

<h1 id="change-change-change-change">Change, change change change</h1>

<p>What we don’t want:</p>
<ul>
  <li>Characters switching weapons between attacks without penalty</li>
  <li>Characters switching weapons multiple times per turn without penalty</li>
</ul>

<p>So let’s say:</p>
<blockquote>
  <p>You can switch weapon whenever you want but the next attack is made with a disadvantage</p>
</blockquote>

<p>There is good things about this rule:</p>
<ul>
  <li>Character with multiple attacks are less disadvantaged than others wince a lower ratio of their attacks will be made with disadvantaged. Martial character should be better in combat so it doesn’t bother me.</li>
</ul>

<p>There is a few problems with this rule:</p>
<ul>
  <li>It allows a caster to switch weapon and, after that, cast a spell that is not an attack without penalty</li>
  <li>When you are already at disadvantage switching to your melee weapon is free (when you’re holding a ranged weapon in melee for example).</li>
</ul>

<p>So let’s change the rule to:</p>
<blockquote>
  <p>If you would make an attack during your turn without having a disadvantage you can choose to make this attack with a disadvantage and interact with one object or feature of the environment for free as part of this attack.</p>
</blockquote>

<p>Next time we’ll talk about how to cast spells while you hand aren’t free, it’s more complicated than one can think…</p>]]></content><author><name></name></author><category term="dnd" /><category term="dnd5" /><category term="ttrpg" /><category term="en" /><summary type="html"><![CDATA[I introduced a simple rule in my D&amp;D5 campaign about switching weapons but first I’m going to explain how all of this is supposed to work RAW (rules as written).]]></summary></entry><entry xml:lang="en"><title type="html">A Lower Bound Weight System for D&amp;amp;D5</title><link href="https://playest.net/blog/2020/08/14/A-Lower-Bound-Weight-System-for-D&D5.html" rel="alternate" type="text/html" title="A Lower Bound Weight System for D&amp;amp;D5" /><published>2020-08-14T00:00:00+02:00</published><updated>2020-08-14T00:00:00+02:00</updated><id>https://playest.net/blog/2020/08/14/A%20Lower%20Bound%20Weight%20System%20for%20D&amp;D5</id><content type="html" xml:base="https://playest.net/blog/2020/08/14/A-Lower-Bound-Weight-System-for-D&amp;D5.html"><![CDATA[<p>There is 3 way of dealing with <a href="https://dnd.wizards.com/products/tabletop/players-basic-rules">weight in D&amp;D5</a> (open the “Using Each Ability” and read from “Lifting and Carrying”):</p>
<ol>
  <li>Not caring</li>
  <li>Using the <em>normal rule</em>, a character can carry its strength score time 15 in pound</li>
  <li>Use the <em>variant encumbrance</em> rule
    <ul>
      <li>A character can carry up to 5 times its strength score with no problem</li>
      <li>Between 5 and 10 times its strength score (it is <strong>encumbered</strong>) with a 10ft penalty to movement</li>
      <li>Between 10 and 15 times its strength score (it is <strong>heavily encumbered</strong>) with a 20ft penalty to movement <strong>and</strong> you have disadvantage on ability checks, attack rolls, and saving throws that use Strength, Dexterity, or Constitution.</li>
    </ul>
  </li>
</ol>

<p>The thing is, unless you are using something that always compute the total weight of the stuff you are carrying it’s a hassle. What I don’t want in a weight system:</p>
<ol>
  <li>Carrying 100s of arrows, or darts</li>
  <li>Carrying 4 different heavy armors</li>
  <li>Carrying a carriage and 3 barrels on your back</li>
  <li>Carrying a dozens of weapons like great club, pike, heavy crossbow, great axe, swords …</li>
  <li>Having to look for the weight of every object in a table</li>
</ol>

<p>We define the notion of Lower Bound Carrying Capacity (LBCC) so that when you are over your LBCC you are also over your carrying capacity. Basically we are going to have two kind of items:</p>
<ul>
  <li><strong>Bulky</strong> items that we count towards encumbrance</li>
  <li><strong>Small</strong> items that don’t count towards encumbrance but are limited in number</li>
</ul>

<p>Let’s start with the normal rule (“a character can carry its strength score time 15 in pound”). If our strength score is <code class="language-plaintext highlighter-rouge">S</code> we can carry no more than <code class="language-plaintext highlighter-rouge">S</code> items of 15 pound. or <code class="language-plaintext highlighter-rouge">2×S</code> items of 7.5 pounds, or <strong><code class="language-plaintext highlighter-rouge">3×S</code> items of 5 pound</strong>, … you get the idea.</p>

<p>First let’s look at the minimum, maximum and median weight of items by category:</p>

<div class="table-wrapper">

  <table>
    <thead>
      <tr>
        <th>Category</th>
        <th>MIN of Weight (lb)</th>
        <th>MEDIAN of Weight (lb)</th>
        <th>MAX of Weight (lb)</th>
        <th># of items</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Adventuring Gear</td>
        <td>0</td>
        <td>2</td>
        <td>70</td>
        <td>99</td>
      </tr>
      <tr>
        <td>Gaming Set</td>
        <td>0</td>
        <td>1.25</td>
        <td>5</td>
        <td>8</td>
      </tr>
      <tr>
        <td>Mounts and Vehicles</td>
        <td>0</td>
        <td>40</td>
        <td>600</td>
        <td>12</td>
      </tr>
      <tr>
        <td>Simple Ranged Weapons</td>
        <td>0</td>
        <td>1.1</td>
        <td>5</td>
        <td>4</td>
      </tr>
      <tr>
        <td>Martial Ranged Weapons</td>
        <td>1</td>
        <td>3</td>
        <td>18</td>
        <td>5</td>
      </tr>
      <tr>
        <td>Musical instrument</td>
        <td>1</td>
        <td>2</td>
        <td>10</td>
        <td>10</td>
      </tr>
      <tr>
        <td>Simple Melee Weapons</td>
        <td>1</td>
        <td>2</td>
        <td>10</td>
        <td>10</td>
      </tr>
      <tr>
        <td>Tools</td>
        <td>1</td>
        <td>5</td>
        <td>10</td>
        <td>19</td>
      </tr>
      <tr>
        <td>Currency (100 or 1000 coins)</td>
        <td>2</td>
        <td>11</td>
        <td>20</td>
        <td>2</td>
      </tr>
      <tr>
        <td>Martial Melee Weapons</td>
        <td>2</td>
        <td>4</td>
        <td>18</td>
        <td>18</td>
      </tr>
      <tr>
        <td>Shield</td>
        <td>6</td>
        <td>6</td>
        <td>6</td>
        <td>1</td>
      </tr>
      <tr>
        <td>Light Armor</td>
        <td>8</td>
        <td>10</td>
        <td>13</td>
        <td>3</td>
      </tr>
      <tr>
        <td>Medium Armor</td>
        <td>12</td>
        <td>20</td>
        <td>45</td>
        <td>5</td>
      </tr>
      <tr>
        <td>Heavy Armor</td>
        <td>40</td>
        <td>57.5</td>
        <td>65</td>
        <td>4</td>
      </tr>
      <tr>
        <td>Grand Total</td>
        <td>0</td>
        <td>3</td>
        <td>600</td>
        <td>200</td>
      </tr>
    </tbody>
  </table>

</div>

<p>You can see that some of those things weight 600lb, we are going to remove everything 70lb or above from the dataset because they are obviously <strong>bulky</strong> and you don’t really need a rule for them.</p>

<table>
  <thead>
    <tr>
      <th>Category</th>
      <th>Name</th>
      <th>Weight (lb)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Adventuring Gear</td>
      <td>Barrel</td>
      <td>70</td>
    </tr>
    <tr>
      <td>Mounts and Vehicles</td>
      <td>Chariot</td>
      <td>100</td>
    </tr>
    <tr>
      <td>Mounts and Vehicles</td>
      <td>Cart</td>
      <td>200</td>
    </tr>
    <tr>
      <td>Mounts and Vehicles</td>
      <td>Sled</td>
      <td>300</td>
    </tr>
    <tr>
      <td>Mounts and Vehicles</td>
      <td>Wagon</td>
      <td>400</td>
    </tr>
    <tr>
      <td>Mounts and Vehicles</td>
      <td>Carriage</td>
      <td>600</td>
    </tr>
  </tbody>
</table>

<p>Let’s see all that in a bubble graph with a log scale (<em>btw, I couldn’t directly add a text label to the horizontal axes on google sheet, anybody knows if it’s possible?</em>):</p>

<p><img src="/blog/assets/lower%20bound%20weight%20system/weight%20freq%20by%20category.svg" alt="weapon weight frequency by categories" class="center" /></p>

<p>A few things that you can deduce from this chart to help you read it:</p>
<ul>
  <li>All gaming sets weight 5lb or less</li>
  <li>All mounts and vehicles weight weight 8lb or more</li>
</ul>

<h1 id="very-easy-weight-system-aka-vews">Very Easy Weight System aka VEWS</h1>

<p>We can see that a lot of categories are either over or under 5lb, so that may be a good tipping point to declare something <strong>bulky</strong> ou our LBCC weight system:</p>
<ul>
  <li><strong>Small</strong>, less than 5lb:
    <ul>
      <li>Simple melee weapons (except 1 in 10 at)</li>
      <li>Simple ranged weapons</li>
      <li>Martial ranged weapons (except 1 in 4 at 18lb)</li>
      <li>Gaming sets</li>
    </ul>
  </li>
  <li><strong>Bulky</strong>, 5lb or more:
    <ul>
      <li>Tools</li>
      <li>Armors
        <ul>
          <li>Shield</li>
          <li>Light armor</li>
          <li>Medium armor</li>
          <li>Heavy armor</li>
        </ul>
      </li>
      <li>Mounts and vehicles</li>
      <li>What is obviously heavy (Barrel, Chariot, Cart, Sled, Wagon, Carriage)</li>
    </ul>
  </li>
  <li><strong>Undecided</strong> (will be categorized as <strong>small</strong>, read the next paragraph):
    <ul>
      <li>Martial melee weapons</li>
      <li>Musical instruments</li>
      <li>Adventuring gear</li>
      <li>Currency (I would use a separate rule for that anyway)</li>
    </ul>
  </li>
</ul>

<p>We have 4 undecided categories:</p>
<ul>
  <li>I would use a separate weight system for currency (coins) anyway so it’s not a problem that this is undecided</li>
  <li>Lets’s put Martial melee weapons with other weapons in the <strong>small</strong> category, they are a little heavier but it’s more simple that way</li>
  <li>Adventuring gear is very diverse and most of it is <em>light</em> sot let’s put it in the <strong>small</strong> category</li>
  <li>Nobody cares how many musical instruments a character is carrying so let’s put it in the <strong>small</strong> category, and if a character want to have fun carrying multiple instruments I have no problem allowing it anyway</li>
</ul>

<p>There is one thing that I don’t like at this point: weapons are <strong>small</strong> and that’s typically the kind of thing that my players will carry a lot of. That is why we are going to introduce one exception to the rule: groups of any 4 weapons are <strong>bulky</strong>.</p>

<p>So this system, that we’ll call Very Easy Weight System (VEWS), has 4 simple rules:</p>
<ul>
  <li>Everything is <strong>small</strong> (which means it does count in the LBCC weight of the character) except that:</li>
  <li>Tools, armors, mounts and vehicles and items that are obviously heavy are <strong>bulky</strong></li>
  <li>Groups of any 4 weapons are <strong>bulky</strong></li>
  <li>Any group of 20 identical items is <strong>bulky</strong> (rations, arrows, bolts, …)</li>
</ul>

<p>The <em>normal rule</em> (a character can carry its strength score time 15 in pound) becomes a character can carry a number of bulky things equals to 3 times its strength score, so a wizard with 10 strength could carry 30 bulks:</p>
<ul>
  <li>30 armors (30 bulks)</li>
  <li>or 10 armors (10 bulks) and 80 weapons (20 groups of 4 weapons for 20 bulky groups)</li>
</ul>

<p>… which is ridiculous. And useless! So we’re going to have to rethink it.</p>

<h1 id="moderately-easy-weight-system-aka-mews">Moderately Easy Weight System aka MEWS</h1>

<p>Let’s start with the easiest thing, armors:</p>
<ul>
  <li>Heavy armors weight at least 40lb so they now weight 8 bulk</li>
  <li>Medium armor weight at least 12lb so they weight 4 bulk, it should be 2 bulk but medium armor can weight up to 45lb (half-plate) so it’s probably better to go with 4 to avoid under estimate their weight</li>
  <li>Light armors weight at least 10lb so they now weight 2 bulks</li>
</ul>

<p>Which gives us a nice geometrical 2, 4, 8 progression.</p>

<p>Also to avoid carrying character carrying an absurd number of of weapon let’s say that groups of any <strong>2</strong> (previously it was 4) weapons makes a bulk.</p>

<p>And finally, let change the rule for adventuring gear. There is not adventuring gear weighting between 6lb (pole, manacles, fine clothes, note that a shovel weight 5lb) and 10lb (chain, sledge hammer, hempen rope) which gives us a nice mental divide to divide the gear between <strong>bulky</strong> adventuring gear and <strong>light</strong> adventuring gear. At a glance we should be able to judge if an adventuring gear is lighter than a sledge hammer of close in weight to a shovel or heavier than a sledge hammer.</p>

<p>The updated rules for our Moderately Easy Weight System (MEWS) are:</p>
<ol>
  <li>Everything is <strong>small</strong> (which means it does count in the LBCC weight of the character) except that:</li>
  <li>Tools, mounts and vehicles and items that are obviously heavy are <strong>bulky</strong></li>
  <li>Light armors are <strong>bulky</strong>, medium armors are twice that (= 4 bulks) and heavy armors are twice that (= 8 bulks)</li>
  <li>Groups of any 2 weapons are <strong>bulky</strong></li>
  <li>Any group of 10 identical items is <strong>bulky</strong> (rations, arrows, bolts, … and yeah 9 arrows weight nothing)</li>
  <li>Adventuring gear weight nothing if it like a pole or lighter, or be <strong>bulky</strong> if it’s heavier</li>
</ol>

<p>With 30 bulks our 10 strength wizard can now carry:</p>

<ul>
  <li>15 light armors, and many <strong>light</strong> items</li>
  <li>or 3 heavy armors (24 bulks) and 12 weapons (6 groups of 2)</li>
  <li>or a more realist example, for a total of 23 bulks:
    <ul>
      <li>1 heavy armor (8 bulk) looted on a bad guy that she’s bringing back to her friend the paladin</li>
      <li>1 medium armors (4 bulk) that is magical, she’s bringing it back to town to sell it</li>
      <li>4 weapons (2 bulks), two daggers, one quarter staff, one magical thingy</li>
      <li>2 tools (2 bulks), tinkerer and alchemist</li>
      <li>18 rations (1 bulks)</li>
      <li>25 arrows (2 bulks)</li>
      <li>Bedroll and tent (2 bulks)</li>
      <li>Rope (1 bulk)</li>
      <li>Climber’s Kit (1 bulk)</li>
    </ul>
  </li>
</ul>

<p>Which is much more reasonable. The last example is ok for a wizards that is coming back from exploring a dungeon with some loot.</p>

<p>With the MEWS <em>variant encumbrance</em> rule:</p>
<ul>
  <li>A character can carry up to its strength score in bulk with no problem</li>
  <li>Between once and twice its strength score in bulk an be <strong>encumbered</strong>: 10ft penalty to movement</li>
  <li>Between twice and 3 times its strength score in bulk and be <strong>heavily encumbered</strong>: 20ft penalty to movement <strong>and</strong> disadvantage on ability checks, attack rolls, and saving throws that use Strength, Dexterity, or Constitution.</li>
</ul>

<p>These rules are more complicated than I would like but they have the advantage of using simple numbers and being possible to learn. Which mean that, with time, the GM and the players should be able to use them without thinking about it too much.</p>

<p>What I would like to do now is building an inventory sheet that makes counting weight easier, maybe a sheet with the 6 rules on top and a table with blocks of 4 bulks with dashed lines every two bulk and dotted lines for the remaining lines.</p>

<p><a href="/blog/assets/lower%20bound%20weight%20system/inventory%20sheet.pdf"><img src="/blog/assets/lower%20bound%20weight%20system/inventory%20sheet.jpg" alt="inventory sheet screenshot" class="center" /></a></p>

<p><em>Inventory sheet in <a href="/blog/assets/lower%20bound%20weight%20system/inventory%20sheet.odt">odt format</a></em>.</p>

<p>I didn’t test this system so all of this is entirely speculative.</p>

<p>As usual, the data I computed in this post are <a href="/blog/assets/lower%20bound%20weight%20system/items%20and%20weight.ods">available in ods format</a>.</p>]]></content><author><name></name></author><category term="dnd" /><category term="dnd5" /><category term="ttrpg" /><category term="en" /><summary type="html"><![CDATA[There is 3 way of dealing with weight in D&amp;D5 (open the “Using Each Ability” and read from “Lifting and Carrying”): Not caring Using the normal rule, a character can carry its strength score time 15 in pound Use the variant encumbrance rule A character can carry up to 5 times its strength score with no problem Between 5 and 10 times its strength score (it is encumbered) with a 10ft penalty to movement Between 10 and 15 times its strength score (it is heavily encumbered) with a 20ft penalty to movement and you have disadvantage on ability checks, attack rolls, and saving throws that use Strength, Dexterity, or Constitution.]]></summary></entry></feed>