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
相关产品推荐
相关产品推荐

