name: method-shorthand-jsdoc description: 使用方法简写将辅助函数移入返回对象,以正确保留JSDoc。适用于工厂函数有内部辅助函数时,这些函数应暴露文档给消费者,或当悬停在返回的方法上不显示JSDoc时。 metadata: author: epicenter version: ‘1.0’
方法简写用于JSDoc保留
当工厂函数有仅由返回方法使用的辅助函数时,使用方法简写将它们移到返回对象中。这确保JSDoc注释正确传递给消费者。
问题
你编写一个带有文档齐全的辅助函数的工厂函数:
function createHeadDoc(options: { workspaceId: string }) {
const { workspaceId } = options;
/**
* 获取当前纪元号。
*
* 计算所有客户端提议纪元的最大值。
* 这确保并发碰撞收敛到相同版本。
*
* @returns 当前纪元(如果没有碰撞发生,则为0)
*/
function getEpoch(): number {
let max = 0;
for (const value of epochsMap.values()) {
max = Math.max(max, value);
}
return max;
}
return {
workspaceId,
getEpoch, // JSDoc在悬停在返回对象上时不可见!
bumpEpoch(): number {
const next = getEpoch() + 1; // 调用内部辅助函数
return next;
},
};
}
当你在IDE中悬停在head.getEpoch()上时,你看到…什么都没有。JSDoc丢失了。
解决方案
使用方法简写将辅助函数移到返回对象中:
function createHeadDoc(options: { workspaceId: string }) {
const { workspaceId } = options;
return {
workspaceId,
/**
* 获取当前纪元号。
*
* 计算所有客户端提议纪元的最大值。
* 这确保并发碰撞收敛到相同版本。
*
* @returns 当前纪元(如果没有碰撞发生,则为0)
*/
getEpoch(): number {
let max = 0;
for (const value of epochsMap.values()) {
max = Math.max(max, value);
}
return max;
},
bumpEpoch(): number {
const next = this.getEpoch() + 1; // 使用this.methodName()
return next;
},
};
}
现在悬停在head.getEpoch()上显示完整的JSDoc。
为什么这有效
- JSDoc附加到方法定义位置 - 当方法在返回对象中内联定义时,JSDoc直接附加到TypeScript看到的属性上
- 方法简写使用
function语义 -this绑定到对象,所以this.getEpoch()有效 - 无需单独辅助函数 - 如果仅由兄弟方法使用,它属于同一对象
模式
// 错误:辅助函数单独定义,JSDoc在返回时丢失
function createService(client) {
/** 使用缓存获取用户数据。 */
function fetchUser(id: string) { ... }
return {
fetchUser, // JSDoc对消费者不可见!
getProfile(id: string) {
return fetchUser(id); // 工作,但消费者看不到文档
},
};
}
// 正确:方法简写,JSDoc保留
function createService(client) {
return {
/** 使用缓存获取用户数据。 */
fetchUser(id: string) { ... },
getProfile(id: string) {
return this.fetchUser(id); // 使用this.method()
},
};
}
何时应用
在以下情况使用此模式:
- 辅助函数仅由返回对象中的方法使用
- 你希望消费者悬停在方法上时看到JSDoc
- 辅助函数在返回语句之前不需要被调用
在以下情况保持辅助函数单独:
- 它们在初始化期间被调用(在返回之前)
- 它们由多个工厂函数使用(提取到共享模块)
- 它们真正是内部的,不应暴露
箭头函数无效
箭头函数没有自己的this:
// 错误:箭头函数,this未定义
return {
getEpoch: () => { ... },
bumpEpoch: () => {
this.getEpoch(); // 错误:this未定义!
},
};
// 正确:方法简写有正确的this绑定
return {
getEpoch() { ... },
bumpEpoch() {
this.getEpoch(); // 工作!
},
};
真实示例
来自packages/epicenter/src/core/docs/head-doc.ts:
export function createHeadDoc(options: { workspaceId: string; ydoc?: Y.Doc }) {
const { workspaceId } = options;
const ydoc = options.ydoc ?? new Y.Doc({ guid: workspaceId });
const epochsMap = ydoc.getMap<number>('epochs');
return {
ydoc,
workspaceId,
/**
* 获取当前纪元号。
*
* 计算所有客户端提议纪元的最大值。
* 这确保并发碰撞收敛到相同版本
* 而不跳过纪元号。
*
* @returns 当前纪元(如果没有碰撞发生,则为0)
*/
getEpoch(): number {
let max = 0;
for (const value of epochsMap.values()) {
max = Math.max(max, value);
}
return max;
},
/**
* 碰撞纪元到下一个版本。
*
* @returns 碰撞后的新纪元号
*/
bumpEpoch(): number {
const next = this.getEpoch() + 1;
epochsMap.set(ydoc.clientID.toString(), next);
return next;
},
// ... 其他使用this.getEpoch()的方法
};
}
总结
| 方法 | JSDoc可见? | this有效? |
|---|---|---|
| 单独辅助函数 + 引用 | 否 | 不适用 |
| 返回对象中的箭头函数 | 是 | 否 |
| 返回对象中的方法简写 | 是 | 是 |
方法简写是唯一保留JSDoc并允许方法通过this相互调用的方法。