请问 ts中如何在一个联合类型里收窄类型?

export function getWebGLContext(canvas: HTMLCanvasElement) {
  const params = {
    alpha: true,
    depth: false,
    stencil: false,
    antialias: false,
    preserveDrawingBuffer: false,
  };
  let gl: WebGL2RenderingContext | WebGLRenderingContext = canvas.getContext(
    'webgl2',
    params,
  ) as WebGL2RenderingContext;

  const isWebGL2 = !!gl;
  if (!isWebGL2) {
    gl = canvas.getContext('webgl', params) as WebGLRenderingContext;
  }

  let halfFloat;
  let halfFloatTexType;

  if (isWebGL2) {
    gl.getExtension('EXT_color_buffer_float');
    halfFloatTexType = gl.HALF_FLOAT;
  } else {
    halfFloat = gl.getExtension('OES_texture_half_float');
    halfFloatTexType = halfFloat.HALF_FLOAT_OES;
  }

  return { gl, halfFloatTexType };
}

image.png
请教大佬们 需要如何定义才能使得isWebGL2判断下gl有合理的推断呢。
目前只能想到使用断言,但是这样每个gl就得都弄一下。有什么合理的写法呢?

 halfFloatTexType = (gl as WebGL2RenderingContext).HALF_FLOAT;
阅读 2k
3 个回答

可以先用一个变量直接断言进行类型收窄,之后用这个变量进行操作

const gl2 = gl as WebGL2RenderingContext
halfFloatTexType = gl2.HALF_FLOAT

做个type predicates

function is2(gl: WebGL2RenderingContext | WebGLRenderingContext): gl is WebGL2RenderingContext {
  return (gl as WebGL2RenderingContext).HALF_FLOAT !== undefined
}


...
  if (foo(gl)) {
    gl.getExtension('EXT_color_buffer_float');
    halfFloatTexType = gl.HALF_FLOAT;
  } else {
    halfFloat = gl.getExtension('OES_texture_half_float');
    halfFloatTexType = halfFloat.HALF_FLOAT_OES;
  }

可以点击查看下 canvas.getContext 的几个重载定义,指定 params 可以获得更准确的返回类型。使用 Typescript 时尽量少使用 as

且原代码的逻辑可以更清晰一些,分为两种情况处理就不会有「收窄」类型的问题了,也可以减少不必要的条件判断和变量使用。

斗胆更改下您贴的代码。

export function getWebGLContext(canvas: HTMLCanvasElement) {
  // 根据 `canvas.getContext` 类型定义,指定参数类型可以获得更准确的返回类型
  const params: WebGLContextAttributes = {
    alpha: true,
    depth: false,
    stencil: false,
    antialias: false,
    preserveDrawingBuffer: false,
  };

  // 此处 `webgl2Ctx` 被推断为 `WebGL2RenderingContext | null`
  const webgl2Ctx = canvas.getContext("webgl2", params);

  // == 如非 null, 则按照 `WebGL2RenderingContext` 处理 ==
  if (webgl2Ctx) {
    webgl2Ctx.getExtension("EXT_color_buffer_float");
    return {
      gl: webgl2Ctx,
      halfFloatTexType: webgl2Ctx.HALF_FLOAT,
    };
  }

  // == 如取不到 `WebGL2RenderingContext`, 则按照 `WebGLRenderingContext` 处理 ==

  const webglCtx = canvas.getContext("webgl", params);

  // 注意下 webglCtx 为 null 的情况
  if (!webglCtx) {
    throw new Error("can not get  context!");
  }

  const halfFloat = webglCtx.getExtension("OES_texture_half_float");

  if (!halfFloat) {
    throw new Error("can not get OES_texture_half_float!");
  }

  return {
    gl: webglCtx,
    halfFloatTexType: halfFloat.HALF_FLOAT_OES,
  };
}

当然,这其中对两种 Context 的处理放在两个单独的函数中会更符合单一职责原则,看楼主的取舍了。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题