为什么TypeScript不报错Uint8Array传入期望ArrayBuffer的函数,如何捕获这类错误
问题原因
TypeScript 采用结构类型系统(鸭子类型)做类型校验,只要两个类型的公共属性/方法结构匹配,就会认为二者类型兼容。
标准库中ArrayBuffer的类型定义仅包含byteLength只读属性、slice()方法两个核心成员,而Uint8Array等所有TypedArray类型也原生实现了完全同名、同签名的这两个成员,因此从结构上TS会认为Uint8Array满足ArrayBuffer的类型要求,不会抛出编译错误。
你遇到的运行时异常差异来自Float32Array构造函数的 runtime 分支逻辑:
- 入参为
ArrayBuffer时:直接基于缓冲区的字节内容做二进制映射解析,4字节对应1个Float32值,因此4字节长度的ArrayBuffer会生成length=1的Float32Array - 入参为TypedArray时:将传入TypedArray的每一个元素单独做类型转换,生成和原TypedArray长度相同的新Float32Array,因此长度为4的Uint8Array传入后得到
length=4的Float32Array
解决方法
方法1:编译期拦截(类型烙印)
给ArrayBuffer添加独有的虚拟标记属性,破坏TypedArray和它的结构兼容性,这个标记仅在TS类型层面生效,不会影响运行时:
// 全局声明添加标记,仅需声明一次 declare interface ArrayBuffer { readonly __ArrayBufferBrand: unique symbol } function test(data: ArrayBuffer) { console.log("Bytelength " + data.byteLength); let view = new Float32Array(data); console.log("Size of Float32Array " + view.length); } const uint_array : Uint8Array = new Uint8Array(4); test(uint_array); // 编译期直接报错:类型“Uint8Array”上不存在属性“__ArrayBufferBrand” test(uint_array.buffer); // 正常通过
方法2:运行时校验(类型守卫)
如果需要在运行时也做安全校验,可以搭配类型守卫实现,既可以拦截非法入参,也能辅助TS做类型推导:
function isArrayBuffer(val: unknown): val is ArrayBuffer { return Object.prototype.toString.call(val) === '[object ArrayBuffer]' } function test(data: unknown) { if (!isArrayBuffer(data)) { throw new TypeError('参数必须是ArrayBuffer类型') } // 后续data会被TS自动推导为ArrayBuffer类型 console.log("Bytelength " + data.byteLength); let view = new Float32Array(data); console.log("Size of Float32Array " + view.length); }
内容的提问来源于stack exchange,提问作者mattking
相关产品推荐
相关产品推荐

