核心思路是用构造函数校验必填参数并抛错、提供带默认值的getter接口、私有字段隔离输入且支持安全更新、结合TS类型约束。例如new ApiClient({baseUrl:undefined})报"Missing required config: baseUrl",getTimeout()返回this.config.timeout??5000。

核心思路是:用构造函数约束必填参数 + 空对象/默认值兜底 + 类型守卫提前拦截,避免运行时抛出 Cannot read property 'xxx' of undefined。
强制初始化关键字段,拒绝 null/undefined 入参
在工具类构造函数中对关键参数做存在性校验,不满足直接抛错,把问题暴露在创建阶段而非使用阶段:
- 用
if (obj == null)同时拦截null和undefined - 对嵌套属性(如
config.api.url)不建议在构造器里深检,应交由具体方法按需校验 - 错误信息明确指出缺失字段,比如:
new ApiClient({ baseUrl: undefined }) → "Missing required config: baseUrl"
提供安全的属性访问接口,自动 fallback
不直接暴露原始参数对象,而是封装 getter 方法,内部处理未定义场景:
getTimeout() { return this.config.timeout ?? 5000; }getHeaders() { return { ...this.defaults.headers, ...this.config.headers }; }- 避免在每个业务逻辑里重复写
?.或||,把防御逻辑收归一处
用私有属性隔离原始输入,支持运行时更新
将传入参数存为私有字段(如 #config),对外只暴露经过封装的读取/修改接口:
- 防止外部意外篡改原始配置,破坏一致性
- 提供
updateConfig(newConfig)方法,内部做合并与校验,而非直接赋值 - 若需完全不可变,可用
Object.freeze()冻结私有副本(注意深冻结需额外处理)
结合 TypeScript 类型系统强化约束
利用类型声明让“未初始化”在编译期就不可达:
- 定义
interface RequiredConfig { baseUrl: string; timeout?: number; } - 构造函数参数类型设为
RequiredConfig,TS 会强制传入baseUrl - 对可选字段仍需运行时兜底(因 JS 运行时可能绕过 TS),但已大幅降低出错概率

















