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

推荐订阅源

博客园_首页
J
Java Code Geeks
博客园 - 聂微东
量子位
C
Check Point Blog
T
The Blog of Author Tim Ferriss
T
Tailwind CSS Blog
G
Google Developers Blog
Google DeepMind News
Google DeepMind News
B
Blog
罗磊的独立博客
腾讯CDC
GbyAI
GbyAI
博客园 - 【当耐特】
A
About on SuperTechFans
M
MIT News - Artificial intelligence
U
Unit 42
D
Docker
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Y
Y Combinator Blog
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
S
SegmentFault 最新的问题
有赞技术团队
有赞技术团队

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 👍