TypeScript 中如何在方法上下文内重定义变量的类型?
报错的核心原因是 TypeScript 无法感知 setFloatVec2 这类方法和 this.value 类型的绑定关系,只知道 this.value 是包含 null、基础类型、向量矩阵的联合类型,所以会报属性不存在和可能为 null 的错误。下面是三种可行的解决方案:
方案1:使用泛型类(推荐,类型最安全)
通过泛型将 UniformInput 类的实例类型和对应的 uniform 类型绑定,从根源上确定每个实例的 value 类型,完全不需要类型断言。
import { Mat3, Mat4, Vec2, Vec3, Vec4 } from 'math'; // 先建立WebGL常量和对应值类型的映射表 type UniformTypeMap = { [WebGLRenderingContext['FLOAT']]: number; [WebGLRenderingContext['BOOL']]: boolean; [WebGLRenderingContext['FLOAT_VEC2']]: Vec2; [WebGLRenderingContext['FLOAT_VEC3']]: Vec3; [WebGLRenderingContext['FLOAT_VEC4']]: Vec4; [WebGLRenderingContext['FLOAT_MAT3']]: Mat3; [WebGLRenderingContext['FLOAT_MAT4']]: Mat4; }; type UniformType = keyof UniformTypeMap; // 泛型类,T为当前实例对应的uniform类型常量 class UniformInput<T extends UniformType> { public type: T; // value类型自动映射为UniformTypeMap[T] public value: UniformTypeMap[T]; public location: WebGLUniformLocation; // setValue的入参类型也自动匹配 public setValue: (gl: WebGLRenderingContext, value: UniformTypeMap[T]) => void; constructor (options: { gl: WebGLRenderingContext, type: T, location: WebGLUniformLocation }) { const { gl, type, location } = options; this.type = type; this.location = location; switch (this.type) { case gl.FLOAT_VEC2: this.value = new Vec2(); // 注意绑定this指向,避免调用时this丢失 this.setValue = this.setFloatVec2.bind(this); break; case gl.FLOAT_VEC3: this.value = new Vec3(); this.setValue = this.setFloatVec3.bind(this); break; // 其他类型同理补充 } } private setFloatVec2(gl: WebGLRenderingContext, value: Vec2) { // 这里this.value自动被识别为Vec2类型,无需断言 if (!this.value.equals(value)) { this.value.copy(value); gl.uniform2f(this.location, this.value.x, this.value.y); } } private setFloatVec3(gl: WebGLRenderingContext, value: Vec3) { if (!this.value.equals(value)) { this.value.copy(value); gl.uniform3f(this.location, this.value.x, this.value.y, this.value.z); } } // 其他方法同理 }
方案2:单断言赋值给局部变量(改动最小)
如果不想修改类的整体结构,可以在每个set方法开头仅做一次类型断言,将结果赋值给局部变量,后续直接使用局部变量即可,不需要反复写断言。
private setFloatVec2(gl: WebGLRenderingContext, value: Vec2) { // 仅做一次类型断言 const currentValue = this.value as Vec2; if (!currentValue.equals(value)) { currentValue.copy(value); if (this.location) gl.uniform2f(this.location, currentValue.x, currentValue.y); } }
方案3:通过this参数注解限定方法上下文类型
你可以给set方法添加虚拟的this参数注解,指定该方法被调用时this的value类型,TypeScript会自动识别该方法上下文内的this.value类型,无需额外断言。
// 虚拟的this参数不会影响实际调用时的传参 private setFloatVec2(this: UniformInput & { value: Vec2 }, gl: WebGLRenderingContext, value: Vec2) { if (!this.value.equals(value)) { this.value.copy(value); gl.uniform2f(this.location, this.value.x, this.value.y); } }
额外优化说明
由于你在constructor中给setValue赋值前已经对this.value做了初始化,实际运行时this.value不可能为null,因此可以考虑去掉UniformInputValue类型中的Nullable包装,从类型层面排除null的可能性,避免额外的null检查。
内容的提问来源于stack exchange,提问作者user2331095
相关产品推荐
相关产品推荐

