Token导航 LogoToken导航TokenDH.com
研究检索需要联网github未标认证来源可访问许可证需确认审计通过

push-null-to-perimeter将空值推至周边

Agent Skill

push-null-to-perimeter 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

196

周安装

8

GitHub Stars

2

下载量

63
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:push-null-to-perimeter(将空值推至周边)
来源仓库:https://github.com/marius-townhouse/effective-typescript-skills
仓库路径:skills/push-null-to-perimeter
安装命令:
npx skills add https://github.com/marius-townhouse/effective-typescript-skills --skill push-null-to-perimeter
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/marius-townhouse/effective-typescript-skills --skill push-null-to-perimeter

简介

push-null-to-perimeter 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中快速定位候选结果。

  • 适用于根据关键词、任务场景或来源线索进行信息检索和筛选的场景。
  • 通过 npx skills add 命令从指定 GitHub 仓库安装并使用。
  • 安装前建议确认权限范围和维护状态,注意可能触发联网或文件读写操作。
  • 适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

SKILL.md

Push Null Values to the Perimeter of Your Types

Overview

Design types so values are either completely null or completely non-null, not a mix.

Mixed null states create implicit relationships that are hard to track and lead to scattered null checks and bugs.

When to Use This Skill

  • Designing types with multiple nullable fields
  • Finding null checks scattered throughout code
  • Related values that are null together
  • Debugging "undefined is not an object" errors
  • Refactoring types with many optional properties

The Iron Rule

NEVER design types where null values have implicit relationships.

No exceptions:

  • Not for "it's simpler"
  • Not for "we check at runtime"
  • Not for "the fields are independent"

Detection: The "Mixed Null" Smell

If two values are null together or non-null together, express that in the type.

// ❌ VIOLATION: Implicit relationship between min and max
function extent(nums: number[]) {
  let min: number | undefined;
  let max: number | undefined;

  for (const num of nums) {
    if (min === undefined) {
      min = num;
      max = num;
    } else {
      min = Math.min(min, num);
      max = Math.max(max, num);  // Error! max might be undefined
    }
  }
  return [min, max];  // [number | undefined, number | undefined]
}

// Caller has to deal with all four combinations:
const [min, max] = extent([1, 2, 3]);
// min defined + max defined
// min undefined + max undefined
// min defined + max undefined  <- Impossible but allowed by type!
// min undefined + max defined  <- Impossible but allowed by type!

Solution: All-or-Nothing Types

// ✅ CORRECT: Result is either fully present or fully absent
function extent(nums: number[]): [number, number] | null {
  let result: [number, number] | null = null;

  for (const num of nums) {
    if (!result) {
      result = [num, num];
    } else {
      result = [Math.min(num, result[0]), Math.max(num, result[1])];
    }
  }
  return result;
}

// Caller only has two cases:
const result = extent([1, 2, 3]);
if (result) {
  const [min, max] = result;  // Both guaranteed to exist
}

Example: User with Posts

// ❌ BAD: Mixed nullability
class UserPosts {
  user: UserInfo | null;
  posts: Post[] | null;

  constructor() {
    this.user = null;
    this.posts = null;
  }

  async init(userId: string) {
    this.user = await fetchUser(userId);
    this.posts = await fetchPosts(userId);
  }
}

// At any moment, four states are possible:
// user null + posts null      (before init)
// user null + posts non-null  (during init - race condition!)
// user non-null + posts null  (during init - race condition!)
// user non-null + posts non-null  (after init)

// ✅ GOOD: All-or-nothing
class UserPosts {
  user: UserInfo;
  posts: Post[];

  private constructor(user: UserInfo, posts: Post[]) {
    this.user = user;
    this.posts = posts;
  }

  static async create(userId: string): Promise<UserPosts> {
    const [user, posts] = await Promise.all([
      fetchUser(userId),
      fetchPosts(userId),
    ]);
    return new UserPosts(user, posts);
  }
}

// Only two states: no instance, or fully loaded instance
const userPosts = await UserPosts.create(userId);
console.log(userPosts.user.name);  // Always safe!

Example: API Response

// ❌ BAD: Data and error both optional
interface ApiResponse {
  data?: ResponseData;
  error?: Error;
  loading: boolean;
}

// Confusing states are possible:
const bad: ApiResponse = {
  data: someData,
  error: someError,  // Both data AND error?
  loading: true,     // Still loading but has data?
};

// ✅ GOOD: Each state is complete
type ApiResponse =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: ResponseData }
  | { status: 'error'; error: Error };

// No confusion possible:
function handleResponse(response: ApiResponse) {
  switch (response.status) {
    case 'idle':
      return null;
    case 'loading':
      return <Spinner />;
    case 'success':
      return <Data data={response.data} />;  // data guaranteed
    case 'error':
      return <Error error={response.error} />;  // error guaranteed
  }
}

The Boundary Pattern

Handle nullability at the boundaries of your system, not throughout:

// ❌ BAD: Null checks everywhere
function processUser(userId: string | null) {
  if (!userId) return null;
  const user = getUser(userId);
  if (!user) return null;
  const posts = getPosts(user);
  if (!posts) return null;
  return formatUserWithPosts(user, posts);
}

// ✅ GOOD: Check at boundary, then work with clean types
function processUser(userId: string | null): UserWithPosts | null {
  // Handle null at the boundary
  if (!userId) return null;

  const user = getUser(userId);
  if (!user) return null;

  // After validation, work with non-null types
  return formatUserWithPosts(user);  // Takes User, not User | null
}

function formatUserWithPosts(user: User): UserWithPosts {
  // No null checks needed inside - user is guaranteed non-null
  const posts = user.posts;  // Always exists
  return { ...user, posts: posts.map(formatPost) };
}

Class Design: Fully Initialized or Not At All

// ❌ BAD: Partially initialized state
class Connection {
  socket: Socket | null = null;
  protocol: Protocol | null = null;

  async connect() {
    this.socket = await createSocket();
    this.protocol = await negotiateProtocol(this.socket);
  }

  send(data: string) {
    if (!this.socket || !this.protocol) {
      throw new Error('Not connected');
    }
    this.protocol.send(this.socket, data);
  }
}

// ✅ GOOD: Factory ensures complete initialization
class Connection {
  private constructor(
    private socket: Socket,
    private protocol: Protocol,
  ) {}

  static async create(): Promise<Connection> {
    const socket = await createSocket();
    const protocol = await negotiateProtocol(socket);
    return new Connection(socket, protocol);
  }

  send(data: string) {
    // No null checks - always initialized
    this.protocol.send(this.socket, data);
  }
}

Pressure Resistance Protocol

1. "We Need Partial States"

Pressure: "The object needs to exist before all data is loaded"

Response: Create a separate type for the partial state, or use a factory.

Action: Use discriminated unions or async factories.

2. "It's More Complex"

Pressure: "One type with optional fields is simpler"

Response: Scattered null checks are more complex than clean types.

Action: Invest in the type design upfront.

Red Flags - STOP and Reconsider

  • Multiple optional fields that are null together
  • Null checks scattered throughout a class
  • Race conditions in async initialization
  • Comments like "X is only set when Y is set"
  • "Impossible" states that the type allows

Common Rationalizations (All Invalid)

ExcuseReality
"It's simpler"Scattered null checks aren't simple.
"We check at runtime"Types catch errors at compile time.
"Fields are independent"If they're null together, they're related.

Quick Reference

PatternSolution
Two values null togetherReturn tuple or null
Object with loading stateUse discriminated union
Class with async initUse static factory method
Mixed nullable propertiesGroup into nested object

The Bottom Line

Make null an all-or-nothing proposition.

Design types so a value is either completely present or completely absent. Push null handling to the boundaries of your code. The result is cleaner types, fewer null checks, and fewer bugs.

Reference

Based on "Effective TypeScript" by Dan Vanderkam, Item 33: Push Null Values to the Perimeter of Your Types.

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

33.06%
按下载量换算21

Claude

31.57%
按下载量换算20

Cursor

20.2%
按下载量换算13

Gemini CLI

9.15%
按下载量换算6

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills