TypeScript Footgun — Unconstrained Generics
Tags: typescript, programminglanguages
Published: 2023-07-17 19:18:51

---
title: TypeScript Footgun — Unconstrained Generics
published: true
date: 2023-07-17 15:50:00 UTC
tags: TypeScript,ProgrammingLanguages
canonical_url: https://kevincox.ca/2023/07/17/typescript-unconstrained-generic/
---

<main style="--width:min(36em,100%);display:grid;grid-template-columns:1fr var(--width) 1fr;overflow:auto;padding:0;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><p style="box-sizing:border-box;grid-column:2;margin:0 1rem .8em">Let’s set the scene with some basic types. Our example is going to work with structured logging data.</p>
<div style="background:#222;border-bottom:1px solid #444;border-top:1px solid #444;box-sizing:border-box;display:grid;grid-column:2;grid-column:1/4;grid-template-columns:1fr var(--width) 1fr;margin:0 0 .8em;overflow-x:auto;padding:0"><pre style="box-sizing:border-box;grid-column:2;margin:.4em 1rem"><code style="background:none!important;box-sizing:border-box;color:#fff;margin:0;white-space:pre-wrap;white-space:pre"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">interface</span> <span style="box-sizing:border-box;margin:0;padding:0">Event</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">{</span>
	<span style="box-sizing:border-box;margin:0;padding:0">kind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">:</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">string</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">,</span>
<span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span>

<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">interface</span> <span style="box-sizing:border-box;margin:0;padding:0">LoginEvent</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">extends</span> <span style="box-sizing:border-box;margin:0;padding:0">Event</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">{</span>
	<span style="box-sizing:border-box;margin:0;padding:0">kind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">:</span> <span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">login</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">,</span>
	<span style="box-sizing:border-box;margin:0;padding:0">effective</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">:</span> <span style="box-sizing:border-box;margin:0;padding:0">User</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">,</span>
	<span style="box-sizing:border-box;margin:0;padding:0">actual</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">:</span> <span style="box-sizing:border-box;margin:0;padding:0">User</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">,</span>
<span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span>
</code></pre></div>
<p style="box-sizing:border-box;grid-column:2;margin:0 1rem .8em">In this case we have made our <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">Event</span></code> extensible. We will assume that logs may be published by third party modules or services, so we can’t list them all in the source. We can assume that <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">kind</span></code> is unique across the system. There is a central registry and each <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">kind</span></code> will have a well-defined schema.</p>
<p style="box-sizing:border-box;grid-column:2;margin:0 1rem .8em">We are tasked to create function that takes a list of login event IDs, fetches them from some store, and counts how many are impersonation events. The code could look something like this:</p>
<div style="background:#222;border-bottom:1px solid #444;border-top:1px solid #444;box-sizing:border-box;display:grid;grid-column:2;grid-column:1/4;grid-template-columns:1fr var(--width) 1fr;margin:0 0 .8em;overflow-x:auto;padding:0"><pre style="box-sizing:border-box;grid-column:2;margin:.4em 1rem"><code style="background:none!important;box-sizing:border-box;color:#fff;margin:0;white-space:pre-wrap;white-space:pre"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">export</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">function</span> <span style="box-sizing:border-box;margin:0;padding:0">countImpersonations</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">loginEventIds</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">:</span> <span style="box-sizing:border-box;margin:0;padding:0">EventId</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">[]):</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">number</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">{</span>
	<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">let</span> <span style="box-sizing:border-box;margin:0;padding:0">count</span> <span style="box-sizing:border-box;color:#f5a3a3;font-weight:700;margin:0;padding:0">=</span> <span style="box-sizing:border-box;color:#ffb433;margin:0;padding:0">0</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">;</span>
	<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">for</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">const</span> <span style="box-sizing:border-box;margin:0;padding:0">eventId</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">of</span> <span style="box-sizing:border-box;margin:0;padding:0">loginEventIds</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">)</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">{</span>
		<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">const</span> <span style="box-sizing:border-box;margin:0;padding:0">event</span> <span style="box-sizing:border-box;color:#f5a3a3;font-weight:700;margin:0;padding:0">=</span> <span style="box-sizing:border-box;margin:0;padding:0">fetchEvent</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">eventId</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">);</span>
		<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">if</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">isImpersonation</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">event</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">))</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">{</span>
			<span style="box-sizing:border-box;margin:0;padding:0">count</span> <span style="box-sizing:border-box;color:#f5a3a3;font-weight:700;margin:0;padding:0">+=</span> <span style="box-sizing:border-box;color:#ffb433;margin:0;padding:0">1</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">;</span>
		<span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span>
	<span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span>
	<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">return</span> <span style="box-sizing:border-box;margin:0;padding:0">count</span>
<span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span>
</code></pre></div>
<p style="box-sizing:border-box;grid-column:2;margin:0 1rem .8em">Unfortunately there is a problem here. <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">fetchEvent</span></code> is a generic fetcher so returns <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">Event</span></code>, not the specific <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">LoginEvent</span></code> type that we expect. However, we know that the event is a <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">LoginEvent</span></code> as that is a precondition of this function. The simple solution would be using <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">as</span></code>.</p>
<div style="background:#222;border-bottom:1px solid #444;border-top:1px solid #444;box-sizing:border-box;display:grid;grid-column:2;grid-column:1/4;grid-template-columns:1fr var(--width) 1fr;margin:0 0 .8em;overflow-x:auto;padding:0"><pre style="box-sizing:border-box;grid-column:2;margin:.4em 1rem"><code style="background:none!important;box-sizing:border-box;color:#fff;margin:0;white-space:pre-wrap;white-space:pre"><span style="box-sizing:border-box;margin:0;padding:0">isImpersonation</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">event</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">as</span> <span style="box-sizing:border-box;margin:0;padding:0">LoginEvent</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">)</span>
</code></pre></div>
<p style="box-sizing:border-box;grid-column:2;margin:0 1rem .8em">This works and isn’t too bad. But what if our caller messes up and passes up a non-login <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">EventId</span></code>? It would be better to have a guaranteed clear error instead of hoping that <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">isImpersonation</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">()</span></code> crashes in an obvious way (or even worse succeeds and returns nonsense). A common solution to this is providing “checked cast” functions. For example:</p>
<div style="background:#222;border-bottom:1px solid #444;border-top:1px solid #444;box-sizing:border-box;display:grid;grid-column:2;grid-column:1/4;grid-template-columns:1fr var(--width) 1fr;margin:0 0 .8em;overflow-x:auto;padding:0"><pre style="box-sizing:border-box;grid-column:2;margin:.4em 1rem"><code style="background:none!important;box-sizing:border-box;color:#fff;margin:0;white-space:pre-wrap;white-space:pre"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">function</span> <span style="box-sizing:border-box;margin:0;padding:0">asLoginEvent</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">msg</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">:</span> <span style="box-sizing:border-box;margin:0;padding:0">Event</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">):</span> <span style="box-sizing:border-box;margin:0;padding:0">LoginEvent</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">{</span>
	<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">if</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">msg</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">.</span><span style="box-sizing:border-box;margin:0;padding:0">kind</span> <span style="box-sizing:border-box;color:#f5a3a3;font-weight:700;margin:0;padding:0">!==</span> <span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">login</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">)</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">{</span>
		<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">throw</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">new</span> <span style="box-sizing:border-box;margin:0;padding:0">Error</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">`Wrong event kind, expected login got </span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">${</span><span style="box-sizing:border-box;margin:0;padding:0">msg</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">.</span><span style="box-sizing:border-box;margin:0;padding:0">kind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">`</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">);</span>
	<span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span>
	<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">return</span> <span style="box-sizing:border-box;margin:0;padding:0">msg</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">as</span> <span style="box-sizing:border-box;margin:0;padding:0">LoginEvent</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">;</span>
<span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span>
</code></pre></div>
<p style="box-sizing:border-box;grid-column:2;margin:0 1rem .8em">Now the code turns into the following. We make a quick check and abort with a clear error if something went wrong. Trust, but verify.</p>
<div style="background:#222;border-bottom:1px solid #444;border-top:1px solid #444;box-sizing:border-box;display:grid;grid-column:2;grid-column:1/4;grid-template-columns:1fr var(--width) 1fr;margin:0 0 .8em;overflow-x:auto;padding:0"><pre style="box-sizing:border-box;grid-column:2;margin:.4em 1rem"><code style="background:none!important;box-sizing:border-box;color:#fff;margin:0;white-space:pre-wrap;white-space:pre"><span style="box-sizing:border-box;margin:0;padding:0">isImpersonation</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">asLoginEvent</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">event</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">))</span>
</code></pre></div>
<p style="box-sizing:border-box;grid-column:2;margin:0 1rem .8em">This is a pretty good solution but writing a custom helper for every kind of event is a bit tedious. So we may want to generalize it.</p>
<div style="background:#222;border-bottom:1px solid #444;border-top:1px solid #444;box-sizing:border-box;display:grid;grid-column:2;grid-column:1/4;grid-template-columns:1fr var(--width) 1fr;margin:0 0 .8em;overflow-x:auto;padding:0"><pre style="box-sizing:border-box;grid-column:2;margin:.4em 1rem"><code style="background:none!important;box-sizing:border-box;color:#fff;margin:0;white-space:pre-wrap;white-space:pre"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">function</span> <span style="box-sizing:border-box;margin:0;padding:0">asEventType</span><span style="box-sizing:border-box;color:#f5a3a3;font-weight:700;margin:0;padding:0">&lt;</span><span style="box-sizing:border-box;margin:0;padding:0">T</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">extends</span> <span style="box-sizing:border-box;margin:0;padding:0">Event</span><span style="box-sizing:border-box;color:#f5a3a3;font-weight:700;margin:0;padding:0">&gt;</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">msg</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">:</span> <span style="box-sizing:border-box;margin:0;padding:0">Event</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">,</span> <span style="box-sizing:border-box;margin:0;padding:0">expectedKind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">:</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">string</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">):</span> <span style="box-sizing:border-box;margin:0;padding:0">T</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">{</span>
	<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">if</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">msg</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">.</span><span style="box-sizing:border-box;margin:0;padding:0">kind</span> <span style="box-sizing:border-box;color:#f5a3a3;font-weight:700;margin:0;padding:0">!==</span> <span style="box-sizing:border-box;margin:0;padding:0">expectedKind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">)</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">{</span>
		<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">throw</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">new</span> <span style="box-sizing:border-box;margin:0;padding:0">Error</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">`Wrong event kind, expected </span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">${</span><span style="box-sizing:border-box;margin:0;padding:0">expectedKind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0"> got </span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">${</span><span style="box-sizing:border-box;margin:0;padding:0">msg</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">.</span><span style="box-sizing:border-box;margin:0;padding:0">kind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">`</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">);</span>
	<span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span>
	<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">return</span> <span style="box-sizing:border-box;margin:0;padding:0">msg</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">as</span> <span style="box-sizing:border-box;margin:0;padding:0">T</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">;</span>
<span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span>
</code></pre></div>
<div style="background:#222;border-bottom:1px solid #444;border-top:1px solid #444;box-sizing:border-box;display:grid;grid-column:2;grid-column:1/4;grid-template-columns:1fr var(--width) 1fr;margin:0 0 .8em;overflow-x:auto;padding:0"><pre style="box-sizing:border-box;grid-column:2;margin:.4em 1rem"><code style="background:none!important;box-sizing:border-box;color:#fff;margin:0;white-space:pre-wrap;white-space:pre"><span style="box-sizing:border-box;margin:0;padding:0">isImpersonation</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">asEventType</span><span style="box-sizing:border-box;color:#f5a3a3;font-weight:700;margin:0;padding:0">&lt;</span><span style="box-sizing:border-box;margin:0;padding:0">LoginEvent</span><span style="box-sizing:border-box;color:#f5a3a3;font-weight:700;margin:0;padding:0">&gt;</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">event</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">,</span> <span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">login</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">))</span>
</code></pre></div>
<p style="box-sizing:border-box;grid-column:2;margin:0 1rem .8em">That looks pretty good. There is the obvious danger that we need to keep <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">LoginEvent</span></code> and <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">login</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span></code> in-sync. However as that mapping is static this seems like acceptable risk.</p>
<p style="box-sizing:border-box;grid-column:2;margin:0 1rem .8em">However this function has a <strong style="box-sizing:border-box;margin:0">major</strong> footgun. If you don’t specify the generic parameter TypeScript will infer it, and it will be <strong style="box-sizing:border-box;margin:0">very</strong> generous.</p>
<div style="background:#222;border-bottom:1px solid #444;border-top:1px solid #444;box-sizing:border-box;display:grid;grid-column:2;grid-column:1/4;grid-template-columns:1fr var(--width) 1fr;margin:0 0 .8em;overflow-x:auto;padding:0"><pre style="box-sizing:border-box;grid-column:2;margin:.4em 1rem"><code style="background:none!important;box-sizing:border-box;color:#fff;margin:0;white-space:pre-wrap;white-space:pre"><span style="box-sizing:border-box;margin:0;padding:0">isImpersonation</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">asEventType</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">event</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">,</span> <span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">login</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">));</span>
<span style="box-sizing:border-box;margin:0;padding:0">isImpersonation</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">asEventType</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">event</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">,</span> <span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">Oh no!</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">));</span>
<span style="box-sizing:border-box;margin:0;padding:0">printLogoutEvent</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">asEventType</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">event</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">,</span> <span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">login</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">));</span>
</code></pre></div>
<p style="box-sizing:border-box;grid-column:2;margin:0 1rem .8em">It turns out that the function has some similarities to returning <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">any</span></code>. This means that if one day someone refactors <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">isImpersonation</span></code> to use <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">AuthTokenMintEvent</span></code> rather than <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">LoginEvent</span></code> this code will still compile. Compiler-error drive refactors will lead to bugs because this code will silently keep working.</p>
<p style="box-sizing:border-box;grid-column:2;margin:0 1rem .8em">It is almost as if we wrote the following obviously dangerous code:</p>
<div style="background:#222;border-bottom:1px solid #444;border-top:1px solid #444;box-sizing:border-box;display:grid;grid-column:2;grid-column:1/4;grid-template-columns:1fr var(--width) 1fr;margin:0 0 .8em;overflow-x:auto;padding:0"><pre style="box-sizing:border-box;grid-column:2;margin:.4em 1rem"><code style="background:none!important;box-sizing:border-box;color:#fff;margin:0;white-space:pre-wrap;white-space:pre"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">function</span> <span style="box-sizing:border-box;margin:0;padding:0">asEventType</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">msg</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">:</span> <span style="box-sizing:border-box;margin:0;padding:0">Event</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">,</span> <span style="box-sizing:border-box;margin:0;padding:0">expectedKind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">:</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">string</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">):</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">any</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">{</span>
	<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">if</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">msg</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">.</span><span style="box-sizing:border-box;margin:0;padding:0">kind</span> <span style="box-sizing:border-box;color:#f5a3a3;font-weight:700;margin:0;padding:0">!==</span> <span style="box-sizing:border-box;margin:0;padding:0">expectedKind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">)</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">{</span>
		<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">throw</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">new</span> <span style="box-sizing:border-box;margin:0;padding:0">Error</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">`Wrong event kind, expected </span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">${</span><span style="box-sizing:border-box;margin:0;padding:0">expectedKind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0"> got </span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">${</span><span style="box-sizing:border-box;margin:0;padding:0">msg</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">.</span><span style="box-sizing:border-box;margin:0;padding:0">kind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">`</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">);</span>
	<span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span>
	<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">return</span> <span style="box-sizing:border-box;margin:0;padding:0">msg</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">as</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">any</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">;</span>
<span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span>
</code></pre></div>
<p style="box-sizing:border-box;grid-column:2;margin:0 1rem .8em"><code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">any</span></code> is an incredibly dangerous tool. It basically disables all type checking with no warnings. Having <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">any</span></code> appear anywhere in your codebase is a huge risk. Avoid <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">any</span></code> at all costs.</p>
<p style="box-sizing:border-box;grid-column:2;margin:0 1rem .8em">This function isn’t quite as bad as returning <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">any</span></code> due to TypeScript’s limited inference, but it is close enough to cause trouble. I have seen many production bugs due to helper functions like this.</p>
<div style="background:#222;border-bottom:1px solid #444;border-top:1px solid #444;box-sizing:border-box;display:grid;grid-column:2;grid-column:1/4;grid-template-columns:1fr var(--width) 1fr;margin:0 0 .8em;overflow-x:auto;padding:0"><pre style="box-sizing:border-box;grid-column:2;margin:.4em 1rem"><code style="background:none!important;box-sizing:border-box;color:#fff;margin:0;white-space:pre-wrap;white-space:pre"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">const</span> <span style="box-sizing:border-box;margin:0;padding:0">loginEvent</span> <span style="box-sizing:border-box;color:#f5a3a3;font-weight:700;margin:0;padding:0">=</span> <span style="box-sizing:border-box;margin:0;padding:0">asEventType</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">event</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">,</span> <span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">login</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">);</span>
<span style="box-sizing:border-box;margin:0;padding:0">isImpersonation</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">loginEvent</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">)</span>
<span style="box-sizing:border-box;color:#ccc;font-style:italic;margin:0;padding:0;white-space:pre-wrap">// error TS2345: Argument of type 'Event' is not assignable to parameter of type 'LoginEvent'.</span>
</code></pre></div>
<h2 id="identifying-the-problem" style="box-sizing:border-box;grid-column:2;margin:1.5em 1rem .8em"><a href="https://kevincox.ca/2023/07/17/typescript-unconstrained-generic/#identifying-the-problem" style="box-sizing:border-box;color:inherit!important;margin:0;text-decoration:inherit;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray;text-shadow:inherit!important">Identifying the Problem</a></h2>
<p style="box-sizing:border-box;grid-column:2;margin:0 1rem .8em">The key to this problem is that the generic parameter is not constrained by any arguments. The caller can pick any type and this function promises to output it. How can a function produce types that it doesn’t even know about?</p>
<div style="background:#222;border-bottom:1px solid #444;border-top:1px solid #444;box-sizing:border-box;display:grid;grid-column:2;grid-column:1/4;grid-template-columns:1fr var(--width) 1fr;margin:0 0 .8em;overflow-x:auto;padding:0"><pre style="box-sizing:border-box;grid-column:2;margin:.4em 1rem"><code style="background:none!important;box-sizing:border-box;color:#fff;margin:0;white-space:pre-wrap;white-space:pre"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">function</span> <span style="box-sizing:border-box;margin:0;padding:0">makeValue</span><span style="box-sizing:border-box;color:#f5a3a3;font-weight:700;margin:0;padding:0">&lt;</span><span style="box-sizing:border-box;margin:0;padding:0">T</span><span style="box-sizing:border-box;color:#f5a3a3;font-weight:700;margin:0;padding:0">&gt;</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">():</span> <span style="box-sizing:border-box;margin:0;padding:0">T</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">{</span>
	<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">return</span> <span style="box-sizing:border-box;color:#ccc;font-style:italic;margin:0;padding:0;white-space:pre-wrap">/* ??? */</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">;</span>
<span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span>
</code></pre></div>
<h2 id="solutions" style="box-sizing:border-box;grid-column:2;margin:1.5em 1rem .8em"><a href="https://kevincox.ca/2023/07/17/typescript-unconstrained-generic/#solutions" style="box-sizing:border-box;color:inherit!important;margin:0;text-decoration:inherit;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray;text-shadow:inherit!important">Solutions</a></h2>
<h3 id="bind-generics-to-parameters" style="box-sizing:border-box;grid-column:2;margin:.4em 1rem .8em"><a href="https://kevincox.ca/2023/07/17/typescript-unconstrained-generic/#bind-generics-to-parameters" style="box-sizing:border-box;color:inherit!important;margin:0;text-decoration:inherit;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray;text-shadow:inherit!important">Bind Generics to Parameters</a></h3>
<p style="box-sizing:border-box;grid-column:2;margin:0 1rem .8em">Where possible this is the best solution. It ensures at compile time that the generic matches.</p>
<div style="background:#222;border-bottom:1px solid #444;border-top:1px solid #444;box-sizing:border-box;display:grid;grid-column:2;grid-column:1/4;grid-template-columns:1fr var(--width) 1fr;margin:0 0 .8em;overflow-x:auto;padding:0"><pre style="box-sizing:border-box;grid-column:2;margin:.4em 1rem"><code style="background:none!important;box-sizing:border-box;color:#fff;margin:0;white-space:pre-wrap;white-space:pre"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">function</span> <span style="box-sizing:border-box;margin:0;padding:0">asEventType</span><span style="box-sizing:border-box;color:#f5a3a3;font-weight:700;margin:0;padding:0">&lt;</span><span style="box-sizing:border-box;margin:0;padding:0">T</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">extends</span> <span style="box-sizing:border-box;margin:0;padding:0">Event</span><span style="box-sizing:border-box;color:#f5a3a3;font-weight:700;margin:0;padding:0">&gt;</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">msg</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">:</span> <span style="box-sizing:border-box;margin:0;padding:0">Event</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">,</span> <span style="box-sizing:border-box;margin:0;padding:0">expectedKind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">:</span> <span style="box-sizing:border-box;margin:0;padding:0">T</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">[</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">kind</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">]):</span> <span style="box-sizing:border-box;margin:0;padding:0">T</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">{</span>
	<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">if</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">msg</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">.</span><span style="box-sizing:border-box;margin:0;padding:0">kind</span> <span style="box-sizing:border-box;color:#f5a3a3;font-weight:700;margin:0;padding:0">!==</span> <span style="box-sizing:border-box;margin:0;padding:0">expectedKind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">)</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">{</span>
		<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">throw</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">new</span> <span style="box-sizing:border-box;margin:0;padding:0">Error</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">`Wrong event kind, expected </span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">${</span><span style="box-sizing:border-box;margin:0;padding:0">expectedKind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0"> got </span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">${</span><span style="box-sizing:border-box;margin:0;padding:0">msg</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">.</span><span style="box-sizing:border-box;margin:0;padding:0">kind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">`</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">);</span>
	<span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span>
	<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">return</span> <span style="box-sizing:border-box;margin:0;padding:0">msg</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">as</span> <span style="box-sizing:border-box;margin:0;padding:0">T</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">;</span>
<span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span>

<span style="box-sizing:border-box;margin:0;padding:0">isImpersonation</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">asEventType</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">event</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">,</span> <span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">login</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">));</span>

<span style="box-sizing:border-box;margin:0;padding:0">isImpersonation</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">asEventType</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">event</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">,</span> <span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">auth-token</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">));</span>
<span style="box-sizing:border-box;color:#ccc;font-style:italic;margin:0;padding:0;white-space:pre-wrap">// error TS2345: Argument of type '"auth-token"' is not assignable to parameter of type '"login"'.</span>
</code></pre></div>
<p style="box-sizing:border-box;grid-column:2;margin:0 1rem .8em">Now you don’t even need to specify the parameter explicitly, TypeScript will infer the parameter and complain if the passed in <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">kind</span></code> string doesn’t match the expected value.</p>
<h3 id="as-outside" style="box-sizing:border-box;grid-column:2;margin:.4em 1rem .8em"><a href="https://kevincox.ca/2023/07/17/typescript-unconstrained-generic/#as-outside" style="box-sizing:border-box;color:inherit!important;margin:0;text-decoration:inherit;text-decoration:underline;-webkit-text-decoration-color:gray;text-decoration-color:gray;text-shadow:inherit!important"><code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">as</span></code> Outside</a></h3>
<p style="box-sizing:border-box;grid-column:2;margin:0 1rem .8em">For cases where you can’t strongly type the function my preferred solution is to move the <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">as</span></code> into the caller. Along with <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">any</span></code>, <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">as</span></code> is a key danger of TypeScript. While you will see it in most codebases it is important to use it as safely as possible. Whenever I use <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">as</span></code> I ask “Can any code changes break this assumption?”. If so then it is best to add more checks or avoid <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">as</span></code>. Using <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">as</span></code> to cast to a generic parameter is frequently a fragile assumption. So in this case it makes sense to remove it.</p>
<div style="background:#222;border-bottom:1px solid #444;border-top:1px solid #444;box-sizing:border-box;display:grid;grid-column:2;grid-column:1/4;grid-template-columns:1fr var(--width) 1fr;margin:0 0 .8em;overflow-x:auto;padding:0"><pre style="box-sizing:border-box;grid-column:2;margin:.4em 1rem"><code style="background:none!important;box-sizing:border-box;color:#fff;margin:0;white-space:pre-wrap;white-space:pre"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">function</span> <span style="box-sizing:border-box;margin:0;padding:0">asEventType</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">msg</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">:</span> <span style="box-sizing:border-box;margin:0;padding:0">Event</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">,</span> <span style="box-sizing:border-box;margin:0;padding:0">expectedKind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">:</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">string</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">):</span> <span style="box-sizing:border-box;margin:0;padding:0">Event</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">{</span>
	<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">if</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">msg</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">.</span><span style="box-sizing:border-box;margin:0;padding:0">kind</span> <span style="box-sizing:border-box;color:#f5a3a3;font-weight:700;margin:0;padding:0">!==</span> <span style="box-sizing:border-box;margin:0;padding:0">expectedKind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">)</span> <span style="box-sizing:border-box;color:#999;margin:0;padding:0">{</span>
		<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">throw</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">new</span> <span style="box-sizing:border-box;margin:0;padding:0">Error</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">`Wrong event kind, expected </span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">${</span><span style="box-sizing:border-box;margin:0;padding:0">expectedKind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0"> got </span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">${</span><span style="box-sizing:border-box;margin:0;padding:0">msg</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">.</span><span style="box-sizing:border-box;margin:0;padding:0">kind</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">`</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">);</span>
	<span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span>
	<span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">return</span> <span style="box-sizing:border-box;margin:0;padding:0">msg</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">;</span>
<span style="box-sizing:border-box;color:#999;margin:0;padding:0">}</span>

<span style="box-sizing:border-box;margin:0;padding:0">isImpersonation</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">asEventType</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">(</span><span style="box-sizing:border-box;margin:0;padding:0">event</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">,</span> <span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">login</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">)</span> <span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">as</span> <span style="box-sizing:border-box;margin:0;padding:0">LoginEvent</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">);</span>
</code></pre></div>
<p style="box-sizing:border-box;grid-column:2;margin:0 1rem .8em">The key difference to this code is that the <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">as</span></code> is at the call site. Since the call site is asserting that <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">login</span><span style="box-sizing:border-box;color:#b3d733;margin:0;padding:0">"</span></code> matches <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">LoginEvent</span></code> it makes sense to put the dangerous operator there. If the <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;color:#8585e0;font-weight:700;margin:0;padding:0">as</span></code> is forgotten the code fails to compile because <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">Event</span></code> can’t be passed where a <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">LoginEvent</span></code> is expected. If <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">isImpersonation</span><span style="box-sizing:border-box;color:#999;margin:0;padding:0">()</span></code> is refactored to use <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">AuthTokenMintEvent</span></code> this code will also raise an error as it will continue to evaluate to <code style="background:#000;border:1px solid #555;border-radius:.3em;box-sizing:border-box;color:#fff;margin:0;padding:0 .2em;white-space:pre-wrap"><span style="box-sizing:border-box;margin:0;padding:0">LoginEvent</span></code>. Then the problem can be addressed before a bug is ever committed.</p></main>