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

