This repository has been archived on 2026-06-01. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
nerve/docs/coding-conventions.md
T
xiaoju 1949007c99 refactor: ban optional properties, use T|null + discriminated unions
- Add coding convention: no '?:', use explicit 'T | null'
- ReflexConfig → discriminated union (SenseReflexConfig | WorkflowReflexConfig)
- All optional fields → explicit null (throttle, timeout, interval, on, maxQueue, workflows)
- Add exactOptionalPropertyTypes to tsconfig
- Add lib: ES2022 to tsconfig
- Refactor validateReflexConfig to reduce cognitive complexity

小橘 <xiaoju@shazhou.work>
2026-04-22 07:33:14 +00:00

4.6 KiB

Nerve Coding Conventions

语言与范式

函数式优先

function + type,不用 class + interface

// ✅ Good
type Signal = {
  senseId: string
  value: unknown
  ts: number
}

function createSignal(senseId: string, value: unknown): Signal {
  return { senseId, value, ts: Date.now() }
}

// ❌ Bad
interface ISignal {
  senseId: string
  value: unknown
  ts: number
}

class Signal implements ISignal {
  constructor(public senseId: string, public value: unknown, public ts: number) {}
}

具体规则

规则 说明
type over interface 所有类型定义用 type,不用 interface
function over class 用纯函数 + 闭包,不用 class
this 函数不依赖 this 上下文
无继承 不用 extendsimplementsabstract
组合优先 用函数组合代替继承层次
不可变优先 Readonly<T>as const,避免 mutation
禁用 optional properties 不用 ?:,用 T | null 显式标记可空;多个互斥字段用 discriminated union

禁用 Optional Properties

不使用 ?:,所有可空字段显式标注 T | null

// ✅ Good — 明确表达"可以为空"
type SenseConfig = {
  group: string;
  throttle: string | null;
  timeout: string | null;
}

// ❌ Bad — optional 隐藏了"缺失"和"空值"的区别
type SenseConfig = {
  group: string;
  throttle?: string;
  timeout?: string;
}

当多个字段互斥时,用 discriminated union 代替一堆 optional:

// ✅ Good — 编译器保证 sense 和 workflow 不会同时出现
type ReflexConfig =
  | { kind: "sense"; sense: string; interval: string | null; on: string[] | null }
  | { kind: "workflow"; workflow: string; on: string[] | null }

// ❌ Bad — sense 和 workflow 都 optional,运行时才知道到底填了哪个
type ReflexConfig = {
  sense?: string;
  workflow?: string;
  interval?: string;
  on?: string[];
}

例外

以下场景允许 class:

  • 第三方库要求(如 Drizzle 的 sqliteTable 返回值)
  • Error 子类(class NerveError extends Error

模块与导出

// ✅ Named exports only
export function startEngine(config: EngineConfig): Engine { ... }
export type EngineConfig = { ... }

// ❌ No default exports
export default function startEngine() { ... }
  • 一律 named export,不用 default export
  • 一个模块做一件事,文件名即职责

命名

类型 风格 示例
文件 kebab-case signal-bus.ts
类型 PascalCase SignalBus
函数/变量 camelCase createSignalBus
常量 UPPER_SNAKE MAX_RETRY_COUNT
泛型参数 单字母或描述性 T, TValue

错误处理

// ✅ 用 Result 类型表达可预期的失败
type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E }

function parseSenseConfig(raw: unknown): Result<SenseConfig> {
  // ...
}

// ✅ throw 只用于不可恢复的 bug
function unreachable(x: never): never {
  throw new Error(`Unreachable: ${x}`)
}
  • 可预期的失败用 Result 类型返回
  • throw 只用于 bug(程序员错误),不用于业务逻辑
  • 不用 try-catch 做流程控制

异步

// ✅ async/await,不用 .then() 链
async function runCompute(sense: SenseModule): Promise<Signal | null> {
  const value = await sense.compute(db, peers)
  if (value == null) return null
  return createSignal(sense.id, value)
}

工具链

工具 用途
pnpm 包管理
TypeScript 类型检查(strict mode)
Biome lint + format(替代 ESLint + Prettier)
tsup 打包

常用命令

pnpm run check      # biome check(lint + format 检查)
pnpm run format     # biome format --write(自动修复格式)
pnpm run build      # 全量构建

Monorepo 结构

nerve/
  packages/
    core/           # @nerve/core — 共享类型和工具函数
    cli/            # @nerve/cli — CLI 入口
    daemon/         # @nerve/daemon — 引擎运行时
  docs/             # RFC、convention 等文档
  biome.json        # 根级 Biome 配置
  tsconfig.json     # 根级 TypeScript 配置(composite project references)
  • core 是共享层,clidaemon 都依赖它
  • clidaemon 之间不互相依赖
  • 未来云端版本作为新 package 加入

Commit Convention

<type>(<scope>): <description>

type: feat | fix | refactor | docs | chore | test
scope: core | cli | daemon | rfc-001 | ...

小橘 🍊(NEKO Team)