🔸 Discriminated Unions
A Discriminated Union (also called a tagged union) is a special kind of union that uses a common literal field to determine the shape of the rest of the object.
✅ Basic Example
type Circle = {
kind: "circle";
radius: number;
};
type Square = {
kind: "square";
side: number;
};
type Shape = Circle | Square;Now we can discriminate based on the kind field:
function getArea(shape: Shape): number {
if (shape.kind === "circle") {
return Math.PI * shape.radius ** 2;
} else {
return shape.side ** 2;
}
}⚙️ Why Use Discriminated Unions?
They make your code:
✅ Type-safe
✅ Easier to reason about
✅ Extensible
✅ Great for Redux reducers, form handling, and state machines
🔄 Example: API Responses
<div class="infographic-ts" id="infographic-ts">
<style>
.infographic-ts {
--body-font: system-ui, -apple-system, sans-serif;
--mono-font: ui-monospace, SFMono-Regular, Consolas, "Courier New", monospace;
--ed-bg: #1e1e1e;
--ed-bar: #2a2a2a;
--ed-out: #d4d4d4;
--ed-muted: #8a8a8a;
--ed-ok: #4ec9b0;
--ed-err: #f14c4c;
--run-bg: #2e7d46;
--run-bg-hover: #35924f;
--err-bg: #a33333;
--err-bg-hover: #b83c3c;
--tok-kw: #c586c0;
--tok-type: #4ec9b0;
--tok-str: #ce9178;
--tok-cmt: #808080;
--tok-var: #ffffff;
max-width: 680px;
margin: 0 auto;
box-sizing: border-box;
background: transparent;
font-family: var(--body-font);
}
.infographic-ts * { box-sizing: border-box; }
.ts-header { font-family: 'Georgia', 'Fraunces', serif; font-weight: 700;
font-size: 1.9rem; letter-spacing: 0.01em; color: inherit; text-align: center;
margin: 0 0 1.4rem; }
.code-window { background: var(--ed-bg); border: 1px solid #2f2f2f; border-radius: 8px;
overflow: hidden; margin-bottom: 1rem; }
.code-topbar { display: flex; align-items: center; justify-content: space-between;
padding: 0.5rem 0.75rem; background: var(--ed-bar); }
.code-dots { display: flex; gap: 6px; }
.code-dot { width: 9px; height: 9px; border-radius: 50%; }
.code-dot.r { background: #ff5f57; }
.code-dot.y { background: #febc2e; }
.code-dot.g { background: #28c840; }
.code-filename { font-family: var(--mono-font); font-size: 0.74rem; color: var(--ed-muted); }
.code-body { padding: 0.85rem 1.1rem; font-family: var(--mono-font); font-size: 0.8rem;
line-height: 1.65; color: var(--ed-out); white-space: pre; overflow-x: auto; }
.tok-kw { color: var(--tok-kw); }
.tok-type { color: var(--tok-type); }
.tok-str { color: var(--tok-str); }
.tok-cmt { color: var(--tok-cmt); }
.tok-var { color: var(--tok-var); }
.tok-punc { color: var(--tok-var); }
.call-row { display: flex; justify-content: center; gap: 0.6rem; margin-bottom: 1rem; flex-wrap: wrap; }
.call-btn { font-family: var(--body-font); font-size: 0.82rem; font-weight: 600; color: #fff;
border: none; border-radius: 6px; padding: 0.6rem 1.1rem; cursor: pointer; }
.call-btn.success { background: var(--run-bg); }
.call-btn.success:hover { background: var(--run-bg-hover); }
.call-btn.error { background: var(--err-bg); }
.call-btn.error:hover { background: var(--err-bg-hover); }
.terminal { background: var(--ed-bg); border-radius: 8px; overflow: hidden;
box-shadow: 0 6px 18px rgba(0,0,0,0.25); }
.term-body { font-family: var(--mono-font); font-size: 0.82rem; padding: 0.9rem 1.1rem;
min-height: 3rem; color: var(--ed-out); line-height: 1.75; }
.term-line { opacity: 0; transform: translateY(4px); transition: opacity 0.25s ease, transform 0.25s ease; }
.term-line.is-in { opacity: 1; transform: translateY(0); }
.term-prompt { color: var(--tok-cmt); }
.term-ok { color: var(--ed-ok); font-weight: 600; }
.term-err { color: var(--ed-err); font-weight: 600; }
.term-placeholder { color: var(--ed-muted); }
</style>
<div class="code-window">
<div class="code-topbar">
<div class="code-dots"><span class="code-dot r"></span><span class="code-dot y"></span><span class="code-dot g"></span></div>
<span class="code-filename">response.ts</span>
</div>
<div class="code-body"><span class="tok-kw">type</span> <span class="tok-type">Success</span> <span class="tok-punc">=</span> <span class="tok-punc">{</span>
<span class="tok-var">status</span><span class="tok-punc">:</span> <span class="tok-str">"success"</span><span class="tok-punc">;</span>
<span class="tok-var">data</span><span class="tok-punc">:</span> <span class="tok-type">string</span><span class="tok-punc">[];</span>
<span class="tok-punc">};</span>
<span class="tok-kw">type</span> <span class="tok-type">Error</span> <span class="tok-punc">=</span> <span class="tok-punc">{</span>
<span class="tok-var">status</span><span class="tok-punc">:</span> <span class="tok-str">"error"</span><span class="tok-punc">;</span>
<span class="tok-var">message</span><span class="tok-punc">:</span> <span class="tok-type">string</span><span class="tok-punc">;</span>
<span class="tok-punc">};</span>
<span class="tok-kw">type</span> <span class="tok-type">APIResponse</span> <span class="tok-punc">=</span> <span class="tok-type">Success</span> <span class="tok-punc">|</span> <span class="tok-type">Error</span><span class="tok-punc">;</span>
<span class="tok-kw">function</span> <span class="tok-var">handleResponse</span><span class="tok-punc">(</span><span class="tok-var">res</span><span class="tok-punc">:</span> <span class="tok-type">APIResponse</span><span class="tok-punc">)</span> <span class="tok-punc">{</span>
<span class="tok-kw">if</span> <span class="tok-punc">(</span><span class="tok-var">res</span><span class="tok-punc">.</span><span class="tok-var">status</span> <span class="tok-punc">===</span> <span class="tok-str">"success"</span><span class="tok-punc">)</span> <span class="tok-punc">{</span>
<span class="tok-var">console</span><span class="tok-punc">.</span><span class="tok-var">log</span><span class="tok-punc">(</span><span class="tok-str">"Data:"</span><span class="tok-punc">,</span> <span class="tok-var">res</span><span class="tok-punc">.</span><span class="tok-var">data</span><span class="tok-punc">);</span>
<span class="tok-punc">}</span> <span class="tok-kw">else</span> <span class="tok-punc">{</span>
<span class="tok-var">console</span><span class="tok-punc">.</span><span class="tok-var">error</span><span class="tok-punc">(</span><span class="tok-str">"Error:"</span><span class="tok-punc">,</span> <span class="tok-var">res</span><span class="tok-punc">.</span><span class="tok-var">message</span><span class="tok-punc">);</span>
<span class="tok-punc">}</span>
<span class="tok-punc">}</span></div>
</div>
<div class="call-row">
<button class="call-btn success" id="successBtn" type="button">handleResponse(success)</button>
<button class="call-btn error" id="errorBtn" type="button">handleResponse(error)</button>
</div>
<div class="terminal">
<div class="term-body" id="termBody"><span class="term-line is-in term-placeholder">console output appears here</span></div>
</div>
<script>
(function () {
var wrapper = document.currentScript.closest('.infographic-ts')
|| document.querySelector('.infographic-ts');
var root = wrapper;
var term = root.querySelector('#termBody');
var cleared = false;
function addLine(html) {
if (!cleared) {
term.innerHTML = '';
cleared = true;
}
var line = document.createElement('div');
line.className = 'term-line';
line.innerHTML = html;
term.appendChild(line);
requestAnimationFrame(function () { line.classList.add('is-in'); });
setTimeout(function () {
term.scrollIntoView({ behavior: 'smooth', block: 'end' });
}, 50);
}
root.querySelector('#successBtn').addEventListener('click', function () {
addLine('<span class="term-prompt">></span> <span class="term-ok">Data: [ \'a\', \'b\', \'c\' ]</span>');
});
root.querySelector('#errorBtn').addEventListener('click', function () {
addLine('<span class="term-prompt">></span> <span class="term-err">Error: Not found</span>');
});
})();
</script>
</div>