扩展Object、String原型时Mocha测试抛出TypeError异常的问题排查
问题原因分析
这个错误的核心是属性描述符冲突:当你直接给Object.prototype.contains赋值时,本质是在用数据描述符(包含value和writable属性)定义这个属性,但你的测试环境中(比如Mocha、Chai或其他依赖库)已经通过Object.defineProperty给Object.prototype添加了contains属性,并且使用了访问器描述符(即get/set方法)。JavaScript不允许同一个属性同时拥有访问器描述符和数据描述符,因此触发了这个类型错误。
而Azure函数运行环境中没有第三方库修改Object.prototype的contains属性,所以你的代码能正常运行。
解决方案
我推荐三种解决思路,按优先级排序:
1. 避免修改内置对象原型(最佳实践)
修改内置对象的原型是公认的不良实践,容易引发命名冲突、for...in循环污染等问题。你可以把这些逻辑改成独立的工具函数:
// utils.ts export const stringContains = (str: string, substr: string): boolean => { return str.includes(substr); }; export const objectContains = (obj: object, key: string): boolean => { // 用call避免obj重写hasOwnProperty的情况 return Object.prototype.hasOwnProperty.call(obj, key); };
然后在业务代码中替换原型调用:
import { stringContains, objectContains } from './utils'; // 如果不再用原型方法,可以删除下面这行 // import 'custom/prototype/file'; export const myFunction: Function = (s: string): Object => { const bool: boolean = stringContains(s, 'look for this string'); const obj = { name: "string" }; const fbool: boolean = objectContains(obj, 'name'); return { foo: s, fboo: fbool }; }
2. 安全地修改原型(如果必须保留原型方法)
如果你一定要保留原型方法,需要先检查属性是否存在,并用Object.defineProperty显式定义描述符,确保和已有描述符兼容:
declare global { interface String { contains(s: string): boolean; } interface Object { contains(s: string): boolean; } } // String原型扩展 if (!String.prototype.hasOwnProperty('contains')) { Object.defineProperty(String.prototype, 'contains', { value: function(this: string, s: string): boolean { return this.includes(s); }, writable: true, enumerable: false, // 关键:避免污染for...in循环 configurable: true }); } // Object原型扩展 if (!Object.prototype.hasOwnProperty('contains')) { Object.defineProperty(Object.prototype, 'contains', { value: function(this: object, s: string): boolean { return Object.prototype.hasOwnProperty.call(this, s); }, writable: true, enumerable: false, configurable: true }); } export {};
这里的关键点:
- 用
hasOwnProperty检查属性是否已存在,避免重复定义 - 设置
enumerable: false,防止原型方法出现在for...in循环中 - 显式定义描述符,避免和已有属性的描述符冲突
3. 排查测试环境依赖
你可以检查测试环境的依赖包,看看是哪个库修改了Object.prototype的contains属性。比如查看node_modules中的库代码,或者升级/替换冲突的依赖。不过这种方法比较繁琐,优先级低于前两种。
内容的提问来源于stack exchange,提问作者Kim Kong
相关产品推荐
相关产品推荐

