You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript如何用asserts实现常量逻辑断言 避免嵌套可选属性访问报错

TypeScript 深层可选属性断言工具实现方案

我们可以通过TS的asserts类型谓词或者普通类型谓词,实现通用的深层路径校验工具,校验通过后TS会自动缩窄对象类型,后续访问对应路径无需添加非空断言。

1. 断言抛出异常版本

适合路径必须存在、不存在直接中断逻辑的场景:

// 按需要拓展更多层级的函数重载即可
function assertPathExists<T, K1 extends keyof NonNullable<T>>(
  obj: T,
  key1: K1
): asserts obj is T & Record<K1, NonNullable<NonNullable<T>[K1]>>;
function assertPathExists<T, K1 extends keyof NonNullable<T>, K2 extends keyof NonNullable<NonNullable<T>[K1]>>(
  obj: T,
  key1: K1,
  key2: K2
): asserts obj is T & Record<K1, Record<K2, NonNullable<NonNullable<NonNullable<T>[K1]>[K2]>>>;
function assertPathExists<
  T,
  K1 extends keyof NonNullable<T>,
  K2 extends keyof NonNullable<NonNullable<T>[K1]>,
  K3 extends keyof NonNullable<NonNullable<NonNullable<T>[K1]>[K2]>
>(
  obj: T,
  key1: K1,
  key2: K2,
  key3: K3
): asserts obj is T & Record<K1, Record<K2, Record<K3, NonNullable<NonNullable<NonNullable<NonNullable<T>[K1]>[K2]>[K3]>>>>;
function assertPathExists<
  T,
  K1 extends keyof NonNullable<T>,
  K2 extends keyof NonNullable<NonNullable<T>[K1]>,
  K3 extends keyof NonNullable<NonNullable<NonNullable<T>[K1]>[K2]>,
  K4 extends keyof NonNullable<NonNullable<NonNullable<NonNullable<T>[K1]>[K2]>[K3]>
>(
  obj: T,
  key1: K1,
  key2: K2,
  key3: K3,
  key4: K4
): asserts obj is T & Record<K1, Record<K2, Record<K3, Record<K4, NonNullable<NonNullable<NonNullable<NonNullable<NonNullable<T>[K1]>[K2]>[K3]>[K4]>>>>;

// 运行时实现
function assertPathExists(obj: unknown, ...keys: string[]): void {
  let current = obj;
  for (const key of keys) {
    if (current == null || typeof current !== 'object' || !(key in current)) {
      throw new Error(`Path \`${keys.join('.')}\` does not exist`);
    }
    current = (current as Record<string, unknown>)[key];
  }
}

使用示例:

function render() {
  try {
    assertPathExists(data, 'my', 'very', 'complex', 'structure');
  } catch {
    // 路径不存在时的兜底逻辑
    return null;
  }
  return (
    <div>
      {/* TS不会报错,无需非空断言 */}
      <div>{data.my.very.complex.structure}</div>
    </div>
  )
}

2. 返回布尔值版本

更贴合原有条件渲染的场景,无需捕获异常:

// 按需要拓展更多层级的函数重载即可
function checkPathExists<T, K1 extends keyof NonNullable<T>>(
  obj: T,
  key1: K1
): obj is T & Record<K1, NonNullable<NonNullable<T>[K1]>>;
function checkPathExists<T, K1 extends keyof NonNullable<T>, K2 extends keyof NonNullable<NonNullable<T>[K1]>>(
  obj: T,
  key1: K1,
  key2: K2
): obj is T & Record<K1, Record<K2, NonNullable<NonNullable<NonNullable<T>[K1]>[K2]>>>;
function checkPathExists<
  T,
  K1 extends keyof NonNullable<T>,
  K2 extends keyof NonNullable<NonNullable<T>[K1]>,
  K3 extends keyof NonNullable<NonNullable<NonNullable<T>[K1]>[K2]>
>(
  obj: T,
  key1: K1,
  key2: K2,
  key3: K3
): obj is T & Record<K1, Record<K2, Record<K3, NonNullable<NonNullable<NonNullable<NonNullable<T>[K1]>[K2]>[K3]>>>>;
function checkPathExists<
  T,
  K1 extends keyof NonNullable<T>,
  K2 extends keyof NonNullable<NonNullable<T>[K1]>,
  K3 extends keyof NonNullable<NonNullable<NonNullable<T>[K1]>[K2]>,
  K4 extends keyof NonNullable<NonNullable<NonNullable<NonNullable<T>[K1]>[K2]>[K3]>
>(
  obj: T,
  key1: K1,
  key2: K2,
  key3: K3,
  key4: K4
): obj is T & Record<K1, Record<K2, Record<K3, Record<K4, NonNullable<NonNullable<NonNullable<NonNullable<NonNullable<T>[K1]>[K2]>[K3]>[K4]>>>>;

// 运行时实现
function checkPathExists(obj: unknown, ...keys: string[]): boolean {
  let current = obj;
  for (const key of keys) {
    if (current == null || typeof current !== 'object' || !(key in current)) {
      return false;
    }
    current = (current as Record<string, unknown>)[key];
  }
  return true;
}

使用示例:

function render() {
  return (
    <div>
      {checkPathExists(data, 'my', 'very', 'complex', 'structure') && (
        <div>{data.my.very.complex.structure}</div>
      )}
    </div>
  )
}

内容的提问来源于stack exchange,提问作者Andrey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 20:36:03