TypeScript+React中自定义myImage类遍历转为img标签致属性丢失问题咨询
嘿,这问题挺典型的,我来帮你拆解下原因,再给你几个实用的解决思路~
核心原因
你遇到的问题本质是TypeScript类型系统和浏览器运行时行为的不匹配,再加上原生DOM元素继承的特殊性,具体来说:
TypeScript类型是编译时的,不影响运行时
你可能只是把上传得到的图片数组做了myImage[]的类型断言(或者TypeScript自动推断出错),但实际上数组里的元素还是浏览器原生的HTMLImageElement对象,并不是你自定义的myImage类的实例。TypeScript的类型注解只负责编译时检查,没办法在运行时把原生对象转换成你的子类实例。原生DOM元素的继承有特殊限制
浏览器的Image(也就是HTMLImageElement)是内置的DOM元素构造函数,直接用class myImage extends Image继承它,在运行时可能不会按预期工作——原生DOM元素的实例是由浏览器内部创建的,无法通过普通的类继承方式扩展出带有自定义属性的实例,哪怕你写了oriHeight属性,也不会自动挂载到原生的图片对象上。控制台显示的小误导
你在浏览器控制台看到的oriHeight,大概率是你在加载图片时手动给原生img对象添加的属性(比如img.oriHeight = img.naturalHeight),但因为这些对象不是myImage的实例,当你在map遍历中访问result.oriHeight时,可能因为异步加载顺序、或者属性挂载时机的问题,导致属性还没被赋值,或者被某些操作覆盖了。
解决方案
根据你的需求,有两种常见的处理方式:
方式一:不用类继承,用接口描述图片结构
既然原生DOM元素不好直接继承,不如用TypeScript的接口来描述带有自定义属性的图片对象,这样更贴合浏览器的实际行为:
interface MyImage extends HTMLImageElement { oriHeight: number; } // 上传图片后,给原生img对象添加oriHeight属性 const results: MyImage[] = []; // 假设你用FileReader加载图片: function loadImage(file: File) { return new Promise<MyImage>((resolve) => { const img = new Image(); img.onload = () => { (img as MyImage).oriHeight = img.naturalHeight; // 手动添加属性 resolve(img as MyImage); }; img.src = URL.createObjectURL(file); }); } // 之后遍历的时候就能正常访问oriHeight了 results.map((result: MyImage) => { console.log(result.oriHeight); // 能正确拿到值 });
方式二:用组合而非继承,封装图片数据
如果一定要用类来管理图片的自定义属性,建议用组合的方式,把原生img对象作为类的一个属性,而不是继承它:
class MyImage { element: HTMLImageElement; oriHeight: number; constructor(imgElement: HTMLImageElement) { this.element = imgElement; this.oriHeight = imgElement.naturalHeight; } } // 上传图片后创建MyImage实例 const results: MyImage[] = []; function loadImage(file: File) { return new Promise<MyImage>((resolve) => { const img = new Image(); img.onload = () => { const myImg = new MyImage(img); resolve(myImg); }; img.src = URL.createObjectURL(file); }); } // 遍历的时候访问类的属性 results.map((result: MyImage) => { console.log(result.oriHeight); // 正常输出 // 如果需要操作DOM元素,用result.element即可 });
额外提示
调试的时候,可以在map里加一句console.log(result instanceof myImage),你会发现输出是false,这就直接证明了这些对象根本不是你自定义类的实例——这就是问题的核心~另外处理图片上传时一定要注意异步加载,确保onload事件触发后再把图片加入results数组,不然oriHeight可能还没被赋值。
内容的提问来源于stack exchange,提问作者Zhang Gloson

