🪟 Windows TippsThe Gemini desktop app is now available for Windows(11.09.2026 um 17:06 Uhr)
🪟 Windows TippsSetting up live captions stuck in Windows 11(14.09.2026 um 16:31 Uhr)
🕵️ SicherheitslückenBurn Out, Or Fade Away(14.09.2026 um 14:25 Uhr)
🪟 Windows TippsWindows 11's latest update broke my speakers, and I'm not alone(14.09.2026 um 18:54 Uhr)
🪟 Windows TippsThe Gemini desktop app is now available for Windows(11.09.2026 um 17:06 Uhr)
🪟 Windows TippsSetting up live captions stuck in Windows 11(14.09.2026 um 16:31 Uhr)
🕵️ SicherheitslückenBurn Out, Or Fade Away(14.09.2026 um 14:25 Uhr)
🪟 Windows TippsWindows 11's latest update broke my speakers, and I'm not alone(14.09.2026 um 18:54 Uhr)

🔧 Programmierung 🕛 vor 3 Monaten 6 Min Lesezeit
0

The Art of the Side Project: How to Ship Fast and Validate Early

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht




7 TypeScript Patterns I Use in Every Project



These aren't groundbreaking. They're the boring patterns that prevent bugs and save hours of debugging.






1. The Result Type (No More try/catch Hell)






CODE
// Instead of throwing exceptions everywhere:
function divide(a: number, b: number): number {
if (b === 0) throw new Error("Division by zero");
return a / b;
}

// Use a Result type:
type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };

function divide(a: number, b: number): Result<number, string> {
if (b === 0) return { ok: false, error: "Division by zero" };
return { ok: true, value: a / b };
}

// Usage — the compiler FORCES you to handle errors:
const result = divide(10, 0);
if (result.ok) {
console.log(result.value); // TypeScript knows this is number
} else {
console.error(result.error); // TypeScript knows this is string
}






Why this matters: No more "undefined is not a function" because you forgot a try/catch. The type system enforces error handling.






2. Branded Types (Prevent Mixing Up IDs)






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

// Easy to mix up:
getUser(postId); // TypeScript allows this! But it's wrong!

// The fix — branded types:
type UserId = string & { readonly __brand: 'UserId' };
type PostId = string & { readonly __brand: 'PostId' };

function createUser(id: string): UserId {
return id as UserId;
}

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

const userId = createUser('abc');
getUser(userId); // ✅ Works
// getPost(userId); // ❌ TypeScript error! Type 'UserId' is not assignable to 'PostId'






When to use: Anytime you have multiple IDs that look the same (userId, orderId, sessionId) but mean different things.






3. The Builder Pattern for Complex Objects






CODE
interface Config {
host: string;
port: number;
database: string;
ssl: boolean;
poolSize: number;
timeout: number;
}

class ConfigBuilder {
private config: Partial<Config> = {};

static create() {
return new ConfigBuilder();
}

withHost(host: string) {
this.config.host = host;
return this; // Chain!
}

withPort(port: number) {
this.config.port = port;
return this;
}

withDatabase(database: string) {
this.config.database = database;
return this;
}

withSSL(ssl = true) {
this.config.ssl = ssl;
return this;
}

withPoolSize(size: number) {
this.config.poolSize = size;
return this;
}

build(): Config {
if (!this.config.host) throw new Error('host is required');
if (!this.config.database) throw new Error('database is required');

return {
host: this.config.host,
port: this.config.port ?? 5432,
database: this.config.database,
ssl: this.config.ssl ?? false,
poolSize: this.config.poolSize ?? 10,
timeout: this.config.timeout ?? 5000,
};
}
}

// Usage — readable, flexible, with defaults:
const config = ConfigBuilder.create()
.withHost('localhost')
.withDatabase('myapp')
.withSSL()
.withPoolSize(20)
.build();









4. Strict Event Emitter Types






CODE
// Instead of the loose EventEmitter from Node:
import { EventEmitter } from 'events';

const emitter = new EventEmitter();
emitter.on('data', (payload: unknown) => { /* type safety? nah */ });
emitter.emit('data', 123); // No type check
emitter.emit('typo', 'anything'); // No type check

// Strict typed version:
type EventMap = {
'user:login': { userId: string; timestamp: Date };
'user:logout': { userId: string };
'error': { message: string; code: number };
'data:update': { key: string; value: unknown };
};

class TypedEmitter<Events extends Record<string, unknown>> {
private emitter = new EventEmitter();

on<K extends keyof Events & string>(
event: K,
handler: (payload: Events[K]) => void
) {
this.emitter.on(event, handler as (...args: unknown[]) => void);
return () => this.emitter.off(event, handler as (...args: unknown[]) => void);
}

emit<K extends keyof Events & string>(
event: K,
payload: Events[K]
) {
this.emitter.emit(event, payload);
}
}

// Usage — fully typed:
const bus = new TypedEmitter<EventMap>();

bus.on('user:login', (payload) => {
console.log(payload.userId); // TypeScript knows this is string
console.log(payload.timestamp); // TypeScript knows this is Date
});

bus.on('error', (payload) => {
console.log(payload.code); // TypeScript knows this is number
});

// bus.emit('user:login', { userId: 123 }); // ❌ Error: string expected
// bus.emit('nonexistent', {}); // ❌ Error: not in EventMap









5. The Guard Pattern (Narrow Types in Functions)






CODE
interface User {
id: string;
name: string;
role: 'admin' | 'editor' | 'viewer';
email?: string; // Might not be set
}

// Instead of checking everywhere:
function sendEmail(user: User) {
if (!user.email) throw new Error('No email');
// TypeScript still thinks email might be undefined here in some cases
}

// Use type guards:
function hasEmail(user: User): user is User & { email: string } {
return typeof user.email === 'string' && user.email.length > 0;
}

function isAdmin(user: User): user is User & { role: 'admin' } {
return user.role === 'admin';
}

// Now TypeScript knows:
function sendEmail(user: User) {
if (!hasEmail(user)) return; // Guard clause
console.log(user.email.toLowerCase()); // ✅ TypeScript knows email is string
}

function deleteEverything(user: User) {
if (!isAdmin(user)) throw new Error('Unauthorized');
// ✅ TypeScript knows role is 'admin' here
}









6. Zod for Runtime Validation






CODE
import { z } from 'zod';

// Define schema once — use for both types AND runtime validation
const UserSchema = z.object({
id: z.string().uuid(),
name: z.string().min(1).max(100),
email: z.string().email(),
role: z.enum(['admin', 'editor', 'viewer']),
createdAt: z.string().datetime(),
});

// Infer TypeScript type from schema
type User = z.infer<typeof UserSchema>;

// Validate API responses at runtime:
async function fetchUser(id: string): Promise<User> {
const response = await fetch(`/api/users/${id}`);
const data = await response.json();

// This throws if data doesn't match schema
return UserSchema.parse(data);
}

// Safe parse (doesn't throw):
const result = UserSchema.safeParse(mysteryData);
if (result.success) {
const user: User = result.data;
} else {
console.error(result.error.flatten());
}






Why Zod over Joi/ Yup? Type inference. Your schema IS your type definition. No duplication.






7. The Async Pool Pattern






CODE
// When you need to run many async operations but limit concurrency:
async function asyncPool<T>(
concurrency: number,
items: T[],
fn: (item: T, index: number) => Promise<void>
): Promise<void> {
const executing = new Set<Promise<void>>();

for (const [index, item] of items.entries()) {
const promise = fn(item, index).then(() => {
executing.delete(promise);
});
executing.add(promise);

if (executing.size >= concurrency) {
await Promise.race(executing);
}
}

await Promise.all(executing);
}

// Usage — fetch 100 URLs but only 5 at a time:
const urls = Array.from({ length: 100 }, (_, i) => `https://api.example.com/page/${i}`);

await asyncPool(5, urls, async (url) => {
const response = await fetch(url);
if (!response.ok) console.error(`Failed: ${url}`);
});

// Compare with naive approach (all at once — might crash):
// await Promise.all(urls.map(url => fetch(url))); // 💥 Too many connections









Bonus: The Never-Returning Function






CODE
// For functions that always throw (assertions, unreachable code):
function assert(condition: unknown, message: string): asserts condition {
if (!condition) throw new Error(message);
}

function unreachable(x: never): never {
throw new Error(`Unreachable code reached: ${x}`);
}

// Usage:
type Status = 'loading' | 'success' | 'error';

function handleStatus(status: Status): string {
switch (status) {
case 'loading': return 'Loading...';
case 'success': return 'Done!';
case 'error': return 'Failed!';
}
// If you add a new status type, TypeScript will error here
// because the switch is no longer exhaustive
}









Which patterns do you use daily? Drop your favorites in the comments.



Follow @armorbreak for more TypeScript and Node.js content.

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
The Gemini desktop app is now available for Windows
1 Quelle
Setting up live captions stuck in Windows 11
1 Quelle
Burn Out, Or Fade Away
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten The Art of the Side Project: How to Ship Fast and Validate Early

Thematisch verwandte Begriffe: Side, Project, Ship, Fast · 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 ...