惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

I
InfoQ
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Apple Machine Learning Research
Apple Machine Learning Research
月光博客
月光博客
B
Blog
罗磊的独立博客
GbyAI
GbyAI
博客园 - 三生石上(FineUI控件)
雷峰网
雷峰网
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Microsoft Security Blog
Microsoft Security Blog
宝玉的分享
宝玉的分享
The GitHub Blog
The GitHub Blog
人人都是产品经理
人人都是产品经理
博客园 - Franky
有赞技术团队
有赞技术团队
WordPress大学
WordPress大学
博客园 - 聂微东
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
Visual Studio Blog
MyScale Blog
MyScale Blog
Google DeepMind News
Google DeepMind News
G
Google Developers Blog
aimingoo的专栏
aimingoo的专栏

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
TypeScript Patterns for Environment Variables
Chocoscoding - Oyeti Timileyin · 2026-06-16 · via DEV Community
Cover image for TypeScript Patterns for Environment Variables

Chocoscoding - Oyeti Timileyin

Yesterday, as I was working on a CORS configuration, AI generated a block of code for me:

const allowedOrigins = [
  process.env.FRONTEND_URL || "http://localhost:3000",
  process.env.ADMIN_URL || "http://localhost:3001",
].filter(Boolean);

I was wondering... why use .filter(Boolean) here? 🤔 The fallbacks already guarantee strings.

So I hovered on the variable. The type definition read:

const allowedOrigins: string[]

Fine. Made sense. But then I got curious. What if I removed the hardcoded fallbacks?

const allowedOrigins = [
  process.env.FRONTEND_URL,
  process.env.ADMIN_URL,
].filter(Boolean);

My type definition changed to:

const allowedOrigins: (string | undefined)[]

I was shocked. I just filtered the array. How can TypeScript still think there's an undefined in there?


First: What Does .filter(Boolean) Even Do?

Boolean used as a filter function removes any falsy value from an array:

false
null
undefined
0
""
NaN

So:

["https://app.com", "", undefined].filter(Boolean)
// Result: ["https://app.com"]

At runtime, this works exactly as you'd expect. No undefined survives. So why does TypeScript disagree? 🤷‍♀️


The Real Answer: TypeScript Doesn't Run Your Code

TypeScript is a transpiler. It doesn't execute .filter(Boolean) — it only looks at types.

When it sees this:

array.filter(Boolean)

It knows the callback returns a boolean. But it doesn't know what that means for the type of the elements that survive. It can't infer "if Boolean(x) is true, then x must be a string." So the undefined stays in the type — even though it'll never actually be there at runtime.

That's the gap: your runtime behavior is correct, but your types are lying.


The Fix: Type Predicates

TypeScript lets you close that gap with a type predicate — a way of explicitly telling the compiler what a filter function guarantees:

const allowedOrigins = [
  process.env.FRONTEND_URL,
  process.env.ADMIN_URL,
].filter((origin): origin is string => Boolean(origin));
// Type: string[] ✅

The origin is string part is the predicate. It's a promise to the compiler: "if this function returns true, the value is definitely a string." TypeScript trusts that and narrows the type accordingly.


The Reusable Helper

If you're doing this pattern often across a codebase, pull it into a small utility:

function isDefined<T>(value: T | undefined | null): value is T {
  return value != null;
}

Then:

const allowedOrigins = [
  process.env.FRONTEND_URL,
  process.env.ADMIN_URL,
].filter(isDefined);
// Type: string[] ✅

Reusable, self-documenting, and sexy 😍. I personally prefer this.


Back to the Original Code

So why did the AI-generated version — with the || fallbacks — give string[] without needing a predicate?

const allowedOrigins = [
  process.env.FRONTEND_URL || "http://localhost:3000",
  process.env.ADMIN_URL || "http://localhost:3001",
].filter(Boolean);

Because process.env.X || "fallback" always evaluates to a string. The fallback string covers the undefined case, so TypeScript already knows every element is a string before the filter runs. The .filter(Boolean) there is just a defensive move — useful if someone later adds an entry without a fallback, but not needed for type correctness.


Quick Reference

  • .filter(Boolean)

    • type def: (string | undefined)[]
    • Use when: You don't care about the resulting type.
  • .filter((x): x is string => Boolean(x))

    • Type def: string[]
    • Use when: Inline, one-off.
  • .filter(isDefined)

    • Type def: string[]
    • Use when: Reusable across a codebase.
  • process.env.X || "fallback"

    • Type def: string
    • Use when: You want a guaranteed default.

The lesson: filter(Boolean) is a runtime thing that TypeScript treats as a black box. When you need your types actually to reflect what's in the array, reach for a type predicate. Small change, honest types.

Thanks for reading 👍