/* Route-Critical (index, V6 §3.6.3 — erzeugt von tools/generate_critical_css.mjs) */ .collapse:not(.show){display:none}.navbar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between}.navbar>.container-fluid{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.mydraft-svg-icon{display:inline-block;vertical-align:middle;color:currentcolor;flex-shrink:0;transition:color var(--duration-fast, 150ms) ease,stroke var(--duration-fast, 150ms) ease,fill var(--duration-fast, 150ms) ease}.mydraft-svg-icon circle,.mydraft-svg-icon line,.mydraft-svg-icon path,.mydraft-svg-icon polygon,.mydraft-svg-icon polyline,.mydraft-svg-icon rect{transition:stroke var(--duration-fast, 150ms) ease,fill var(--duration-fast, 150ms) ease}@layer components{.live-pulse-dot{width:8px;height:8px}}.min-w-0{min-width:0}@layer overrides{.live-radar-header-badge{background:rgba(239,68,68,.12);border:1px solid rgba(239,68,68,.35);color:#fca5a5;font-size:.75rem;font-weight:700;transition:.25s;cursor:pointer}}@layer overrides{.live-pulse-dot{width:8px;height:8px;background-color:var(--threat-critical,#ef4444);border-radius:50%;display:inline-block;box-shadow:rgba(239,68,68,.7) 0 0 0 0}}@layer overrides{body.light .live-radar-header-badge,html[data-bs-theme=light] .live-radar-header-badge{background:#fbd9dd;border-color:#eeb2b8;color:#8a1723!important}}.isharestuff-live-ticker{color:var(--color-text-body,#f8fafc);backdrop-filter:none;flex-wrap:wrap;row-gap:.3rem;background:var(--color-surface,rgba(15,23,42,.85))!important;border:1px solid var(--color-border,rgba(255,255,255,.12))!important;padding:.45rem .5rem!important;margin-bottom:.75rem!important}.ticker-content{mask-image:linear-gradient(to right,transparent 0px,rgb(0,0,0) 14px,rgb(0,0,0) calc(100% - 14px),transparent 100%);touch-action:pan-y;cursor:grab;user-select:none;-webkit-tap-highlight-color:transparent;order:1;width:100%;max-width:100%;flex:0 0 100%!important}.isharestuff-live-ticker .ticker-lead-group{order:2;width:100%;justify-content:space-between;margin-right:0!important}.ticker-track{display:flex;align-items:center;white-space:nowrap;will-change:transform;transform:translate3d(0,0,0);--ticker-speed:1;animation-name:isharestuffTicker;animation-timing-function:linear;animation-iteration-count:infinite;animation-duration:calc(26s / var(--ticker-speed, 1))}.ticker-link{max-width:min(78vw,340px);font-size:.82rem;display:inline-block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:baseline;transition:color .2s;color:var(--color-text-heading,#f8fafc)!important}.isharestuff-live-ticker .ticker-ctrl-btn{min-width:44px;height:44px;font-size:.78rem;-webkit-tap-highlight-color:transparent}.ticker-controls-bar{gap:.4rem}.ticker-counter{font-family:var(--bs-font-monospace, ui-monospace, monospace);font-size:.72rem;line-height:1;color:var(--color-text-secondary,#94a3b8);min-width:3.4em;text-align:center;user-select:none;white-space:nowrap;margin-left:auto}.ticker-item{font-size:.82rem}.ticker-item .ticker-date{font-size:.72rem;vertical-align:baseline}.ticker-text-group{display:inline-flex;align-items:baseline}.ticker-badge-live-btn{border:0;cursor:pointer;text-decoration:none!important}.ticker-badge{font-size:.78rem!important;min-height:44px!important;min-width:44px!important;padding:6px 12px!important;display:inline-flex!important;align-items:center!important;justify-content:center!important}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-toggler{display:none}}@media (prefers-reduced-motion:reduce){@layer overrides{#news-popup,#news-toast-banner,.animate-pulse,.live-pulse-dot,.live-pulsing-dot,.news-item-card{animation:none;transition:none}}.ticker-track{animation:none!important}.ticker-content{cursor:default}}@media (min-width:768px){.isharestuff-live-ticker{flex-wrap:nowrap;row-gap:0;backdrop-filter:blur(12px);padding:.5rem!important;margin-bottom:1rem!important}.isharestuff-live-ticker .ticker-content{order:0;width:auto;max-width:none;flex:1 1 auto!important}.isharestuff-live-ticker .ticker-lead-group{order:0;width:auto;justify-content:flex-start;margin-right:1rem!important}.ticker-controls-bar{gap:.3rem}.isharestuff-live-ticker .ticker-ctrl-btn{min-width:30px;height:auto;font-size:inherit}.ticker-counter{font-size:.66rem;margin-left:0}.ticker-track{animation-duration:calc(40s / var(--ticker-speed, 1))}.ticker-link{max-width:340px;font-size:.85rem}.ticker-item{font-size:.85rem}.ticker-badge{font-size:inherit!important;min-height:0!important;min-width:0!important;padding:.5rem 1rem!important}}@media (max-width:359.98px){.ticker-counter{display:none}.ticker-controls-bar{gap:.3rem}.ticker-badge{padding:6px 9px!important}} 🔒 3 TypeScript patterns I keep stealing from open-source codebases | Sichere Programmierung – TSECURITY.DE Zum Hauptinhalt springen
••
Windows Tipps & SecurityWindows Shell-Lücke: MotW-Bypass aktiv ausgenutzt - Security-Insider(19.10.2026 um 07:01 Uhr)
••
Sichere ProgrammierungA Better Workflow for AI-Assisted Coding: Lessons from Matt Pocock(19.10.2026 um 07:34 Uhr)
•
Sichere ProgrammierungRedactProof & Review: Zero-Cloud Contract Auditing for Freelancers(19.10.2026 um 07:34 Uhr)
•
Sichere ProgrammierungFrom “It Ran” to “Here’s What Happened” in Copilot Studio(19.10.2026 um 07:34 Uhr)
••
Sichere ProgrammierungLedgerling: A private, local-first expense desk for freelancers(19.10.2026 um 07:35 Uhr)
•
Sichere ProgrammierungCreate an AI Voice Agent for Customer Support(19.10.2026 um 07:38 Uhr)
•••
Windows Tipps & SecurityWindows Shell-Lücke: MotW-Bypass aktiv ausgenutzt - Security-Insider(19.10.2026 um 07:01 Uhr)
••
Sichere ProgrammierungA Better Workflow for AI-Assisted Coding: Lessons from Matt Pocock(19.10.2026 um 07:34 Uhr)
•
Sichere ProgrammierungRedactProof & Review: Zero-Cloud Contract Auditing for Freelancers(19.10.2026 um 07:34 Uhr)
•
Sichere ProgrammierungFrom “It Ran” to “Here’s What Happened” in Copilot Studio(19.10.2026 um 07:34 Uhr)
••
Sichere ProgrammierungLedgerling: A private, local-first expense desk for freelancers(19.10.2026 um 07:35 Uhr)
•
Sichere ProgrammierungCreate an AI Voice Agent for Customer Support(19.10.2026 um 07:38 Uhr)
•
Intelligence View
⚡ tsecurity.de Intelligence

3 TypeScript patterns I keep stealing from open-source codebases

Every few months I find a TypeScript pattern in someone's open-source repo that I instantly bring back to my own code. It's the cheapest form of getting better…

Beitrag
0
Seite
0
↗ Quelle (dev.to)
Social ReaktionenReagiere als Erste:r — dein Feedback zählt!

Every few months I find a TypeScript pattern in someone's open-source repo that I instantly bring back to my own code. It's the cheapest form of getting better — read code that's been read by hundreds of contributors and find the patterns that survived.



Here are three I keep reaching for in 2026.






1. satisfies for catch-typos-at-compile-time configs



You know the routine — a config object that holds, say, all your route paths, or all your event names. You want autocomplete when you reference one, but you also want TS to scream if you fat-finger a key.



The old way:




type RouteKey = 'home' | 'blog' | 'profile';
type Routes = Record<RouteKey, string>;

export const routes: Routes = {
home: '/',
blog: '/blog',
profile: '/profile',
};

routes.home; // type is `string` — autocomplete works, but you lose the literal






The satisfies way:




export const routes = {
home: '/',
blog: '/blog',
profile: '/profile',
} satisfies Record<string, string>;

routes.home; // type is the literal '/', not just string






satisfies checks the shape but preserves the specific inferred types of every key and value. If I refer to routes.home, TS knows the value is '/', not just string. Useful when those literals drive other types downstream:




type Path = (typeof routes)[keyof typeof routes];
// Path is '/' | '/blog' | '/profile' — not string






I stole this from a Remix codebase last year. It's in every project I touch now.






2. Branded types to stop ID confusion



Ever passed userId where postId was expected? TypeScript won't catch it, because both are strings.




function getUser(id: string) { /* ... */ }
function getPost(id: string) { /* ... */ }

const userId = 'u_abc';
const postId = 'p_xyz';

getUser(postId); // no error — the bug ships






Brand them and the bug becomes unrepresentable:




type Brand<T, B> = T & { __brand: B };

type UserId = Brand<string, 'UserId'>;
type PostId = Brand<string, 'PostId'>;

function asUserId(s: string): UserId { return s as UserId; }
function asPostId(s: string): PostId { return s as PostId; }

function getUser(id: UserId) { /* ... */ }
function getPost(id: PostId) { /* ... */ }

const userId = asUserId('u_abc');
const postId = asPostId('p_xyz');

getUser(postId); // Error: 'PostId' is not assignable to 'UserId'






The branding is a zero-cost lie — UserId is really just string at runtime — but the compiler treats them as different types. The asUserId helper is the only blessed entry point, so all the unsafe casting happens in one place you can audit.



I picked this up from a fintech repo where mixing up OrderId and TradeId would have been a five-figure mistake. For a side project it's overkill. For anything touching money or user data, I reach for it instantly.






3. Discriminated unions with a kind field



This isn't new, but I use it more than any other pattern on this list, so it belongs.



State that can be in one of several modes — loading / success / error, draft / published / archived, free / pro / enterprise — should be a discriminated union, not three optional booleans.



The painful way:




interface FetchState<T> {
data?: T;
error?: string;
isLoading?: boolean;
}

if (state.data) {
// might still be loading? unclear
// might have a stale error? also unclear
}






The clean way:




type FetchState<T> =
| { kind: 'idle' }
| { kind: 'loading' }
| { kind: 'success'; data: T }
| { kind: 'error'; error: string };

function render(state: FetchState<User>) {
switch (state.kind) {
case 'idle': return null;
case 'loading': return <Spinner />;
case 'success': return <Profile user={state.data} />; // data is typed
case 'error': return <ErrorMessage msg={state.error} />; // error is typed
}
}






Inside each case block, TS narrows the type for you. state.data exists in 'success' and nowhere else. You can't accidentally read it from 'loading' because it isn't on that variant.



The kind field name is convention. Some teams use type, some use status. Pick one, use it everywhere — switching the discriminator name across files makes every code review harder than it needs to be.






What I'd skip



Decorator-heavy patterns. Stage-3 decorators are stable in TS 5+, but every project I've used them in eventually hit a corner where they didn't compose well with the framework's lifecycle. I've gone back to plain functions and higher-order helpers and never missed the magic.






The meta lesson



Reading open-source isn't just for inspiration. It's professional development without the bootcamp tuition. Every pattern in this list survived hundreds of contributors making small calls — that's a stronger filter than my own taste alone.



If you haven't already, pick one TypeScript codebase you admire and read 30 minutes of source this week. Try to spot a pattern you don't already use. Steal it. Use it on Monday.



That's how you get better — one stolen pattern at a time.

Intelligence Digest — kostenlos Täglich die wichtigsten Security-News · jederzeit abbestellbar
Community Rating & Social Proof
⭐ Leser-Wertung
–
Ø / 5.0
Noch keine Leser-Bewertungen
War diese Seite hilfreich?
0
Jetzt Bewertung abgeben
🟢 Niedrig (1-3.9) 🟡 Mittel (4.0-6.9) 🔴 Hoch (7.0-8.9) Kritisch (9.0-10.0)

Verwandte Story-Cluster & Quellen (Vektor-KI)

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten 3 TypeScript patterns I keep stealing from open-source codebases

Thematisch verwandte Begriffe: TypeScript, patterns, keep, stealing · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

💬 Kommentare werden geladen…
Zum Aktualisieren ziehen
Nächster Beitrag