跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

如何通过 JSDoc 条件类型推导消除对象字面量中重复的 @type 注解

本文介绍一种基于 JSDoc 泛型与条件映射类型的技巧,让 TypeScript(或支持 JSDoc 类型检查的编辑器如 VS Code)自动推导代理对象中字符串属性的运行时类型,从而彻底避免在每个对象字段上重复书写 @type {Cypress.Chainable>}。 本文介绍一种基于 js doc 泛型与条件映射类型的技巧,让 typescript(或支持 jsdoc 类型检查的编辑器如 vs code)自动推导代理对象中字符串属性的运行时类型,从而彻底避免在每个对象字段上重复书写 `@type {cypress.chainable >}`。 在 Cypress + Page Object Model(POM)实践中,我们常借助 Proxy 将页面对象中的 CSS 选择器字符串(如 'input'、'[data-testid=logout-button]')动态包装为 cy.get(...) 链式调用对象,以提升类型安全与 IDE 补全体验。但传统写法需为每个字符串字段手动添加 JSDoc 类型注解,既冗余又易出错:
export const homePage = getPageObjectProxy({ /** @type {Cypress.Chainable>} */ searchField: 'input', /** @type {Cypress.Chainable>} */ logOutButton: '[data-testid=logout-button]', // ……更多字段 });
✅ 解决方案:用 JSDoc 条件映射类型替代逐字段注解 核心在于升级 getPageObjectProxy 的 JSDoc 类型声明,利用 keyof T 和 T[key] extends string ? ... : ... 实现「按值类型自动映射」:
/** * @template T * @param {T} pageObject a page object with string selectors * @returns {{[key in keyof T]: T[key] extends string ? Cypress.Chainable> : T[key]}} * A proxied object where string-valued properties are auto-converted to Chainable, others preserved. */ export const getPageObjectProxy = pageObject => new Proxy(pageObject, { get(target, prop) { if (typeof target[prop] === 'string') { return cy.get(target[prop]); } return target[prop]; } });
该返回类型声明表示: 遍历原对象 T 的所有键 keyof T; 对每个键 key,若其原始值类型 T[key] 是 string,则新对象中该键的类型为 Cypress.Chainable>; 否则保持原类型(如方法函数、嵌套对象等)。 使用时,页面对象可完全「零注解」定义: php版微信js-sdk支付接口类 php版微信js-sdk支付接口类 下载
export const homePage = getPageObjectProxy({ searchField: 'input', logOutButton: '[data-testid=logout-button]', modeDropdown: '[data-testid=mode-dropdown]', setMode(name) { // ✅ 直接使用 homePage(而非 this),确保类型上下文正确 homePage.modeDropdown.click(); homePage.modeDropdown.children().contains(name).click(); } });
⚠️ 关键注意事项 方法中避免 this :因 Proxy 的 this 在方法内仍指向原始对象(未被代理),故方法内部须显式引用代理后的对象(如 homePage.modeDropdown),否则类型推导失效且运行时报错; IDE 支持要求 :需启用 checkJs: true(在 jsconfig.json 或 tsconfig.json 中),并确保编辑器为 VS Code 或 WebStorm 等支持 JSDoc 条件类型的工具; 类型守卫局限性 :JSDoc 不支持运行时类型断言,因此 typeof target[prop] === 'string' 仅作逻辑判断,类型映射完全依赖 JSDoc 声明——务必保证输入对象中字符串字段确实为纯 string 字面量; 扩展性提示 :如需支持其他类型转换(如 number → cy.wrap()),可扩展条件类型:T[key] extends string ? ... : T[key] extends number ? ... : T[key]。 通过这一模式,你不仅消除了模板化注解,更将类型契约从「字段级」提升至「函数级」,使代码更简洁、可维护性更强,同时保持完整的智能提示与编译期检查能力。

相关文章