
本文介绍一种基于 JSDoc 条件类型推导的技巧,无需为每个字符串属性手动添加 @type {Cypress.Chainable<JQuery<HTMLElement>>},即可让 TypeScript(或支持 JSDoc 类型检查的编辑器)自动识别代理后对象的正确类型。
本文介绍一种基于 jsdoc 条件类型推导的技巧,无需为每个字符串属性手动添加 `@type {cypress.chainable
在 Cypress + Page Object Model(POM)实践中,我们常借助 Proxy 将原始选择器字符串(如 'input')动态转换为类型安全的 Cypress.Chainable<JQuery<HTMLElement>> 实例,从而兼顾可读性与 IDE 智能提示。但传统写法需为每个字符串字段重复添加 JSDoc @type 注解,既冗余又易出错:
export const homePage = getPageObjectProxy({
/** @type {Cypress.Chainable<JQuery<HTMLElement>>} */
searchField: 'input',
/** @type {Cypress.Chainable<JQuery<HTMLElement>>} */
logOutButton: '[data-testid=logout-button]',
// ...更多字段
});解决方案是:用 JSDoc 的条件映射类型替代逐字段注解。
核心在于增强 getPageObjectProxy 的泛型返回类型声明,利用 T[key] extends string ? X : Y 实现「对所有字符串值属性自动升格为链式查询类型,其余保持原类型」:
/**
* @template T
* @param {T} pageObject a page object with string selectors
* @returns {{[key in keyof T]: T[key] extends string ? Cypress.Chainable<JQuery<HTMLElement>> : T[key]}} page object with DOM elements
*/
export const getPageObjectProxy = pageObject => new Proxy(pageObject, {
get(target, prop) {
if (typeof target[prop] === 'string') {
return cy.get(target[prop]);
}
return target[prop];
}
});✅ 效果说明:
- homePage.searchField 自动被推导为 Cypress.Chainable<JQuery<HTMLElement>>;
- homePage.setMode 仍保持原函数类型;
- IDE(如 VS Code)在 .searchField. 后能完整提示 .click()、.type() 等方法;
- 无需任何 @type 注解,类型安全由泛型约束保障。
⚠️ 注意事项:
- 此方案依赖 JSDoc 类型系统(VS Code + TypeScript 插件默认支持),不适用于纯 JavaScript 运行时校验;
- 方法内部若需访问其他代理属性(如 setMode 中调用 modeDropdown),必须使用 homePage.modeDropdown 而非 this.modeDropdown —— 因为 this 在箭头函数或普通方法中可能指向错误上下文,且代理逻辑不作用于 this 绑定;
- 若对象含混合类型字段(如部分为 number、boolean),条件类型会精准保留其原始类型,确保类型严谨性。
通过这一模式,你不仅消除了模板化注解,更将类型逻辑统一收口至工具函数,使页面对象定义回归简洁语义:只关注「选什么」,无需操心「是什么类型」。


















