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

如何用JSDoc定义允许额外属性且强制指定类型的对象?

解决JSDoc定义允许额外属性且保留类型检查的Person类型问题

问题背景

我在带//@ts-check的JS模块里用JSDoc定义了Person类型作为函数参数,但遇到两个需求冲突的问题:

//@ts-check

/**
 * @typedef {Object} Person
 * @property {number} age
 * @property {string} name
 */

/**
 * Birthday wishes to person
 * @param {Person} person
 */
function birthdayWish(person) {
    return 'Happy birthday number'+person.age+', '+person.name;
}
  • 想要传入带额外属性的对象(比如{name:'Sam', age:35, occupation:'teacher'})时不触发TypeScript错误,但当前会报错
  • 同时要求已声明的age和name必须严格遵循类型,比如{name:'Joe', age:'hmm', occupation:'lawyer'}这种age类型错误的调用必须报错

之前尝试用@typedef {Object.<string, *>} Person加@property的方式,但会完全忽略age和name的类型检查,两次调用都不会报错,求正确的定义方式。

可行解决方案

方案1:在typedef中同时声明明确属性和索引签名

直接在Person的typedef里同时定义必填属性和允许任意额外字符串键的索引签名:

//@ts-check

/**
 * @typedef {Object} Person
 * @property {number} age
 * @property {string} name
 * @property {*} [key: string] // 允许任意额外的字符串属性,值类型不限
 */

/**
 * Birthday wishes to person
 * @param {Person} person
 */
function birthdayWish(person) {
    return 'Happy birthday number'+person.age+', '+person.name;
}

这样定义后:

  • 带occupation的合法调用会正常通过检查
  • age为字符串的非法调用会触发类型错误提示

方案2:用交叉类型拆分基础属性和额外属性

如果需要拆分基础类型和额外属性的定义,可以用TypeScript的交叉类型(&)组合:

//@ts-check

/**
 * @typedef {Object} PersonBase
 * @property {number} age
 * @property {string} name
 */

/**
 * @typedef {PersonBase & { [key: string]: any }} Person
 */

/**
 * Birthday wishes to person
 * @param {Person} person
 */
function birthdayWish(person) {
    return 'Happy birthday number'+person.age+', '+person.name;
}

这个方案和方案1效果一致,适合基础属性较多、需要单独复用的场景。

为什么之前的尝试失败

你之前用@typedef {Object.<string, *>} Person再叠加@property的写法无效,是因为Object.<string, *>本质是一个覆盖所有字符串键的宽泛索引签名,TypeScript会优先使用这个宽泛类型,忽略后面添加的@property声明,导致所有属性的类型检查都被关闭。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 12:02:34