关于ts中,怎将一个参数写为多个类型,并且在函数中能依据不同类型做不同的处理,以我的代码为例,如下,应该怎么改?

以Cesium.ScreenSpaceEventType.LEFT_CLICK和Cesium.ScreenSpaceEventType.MOUSE_MOVE为例,当setInputAction方法的第二个参数为Cesium.ScreenSpaceEventType.LEFT_CLICK时该方法的回调函数的click参数的类型为Cesium.ScreenSpaceEventHandler.PositionedEvent;
setInputAction方法的第二个参数为Cesium.ScreenSpaceEventType.MOUSE_MOVE时该方法的回调函数的click参数的类型为Cesium.ScreenSpaceEventHandler.MotionEvent,现在我已经将第二个参数改为动态的了,那回调函数的click参数类型怎么改呢?
我尝试将

this.handler.setInputAction((click: Cesium.ScreenSpaceEventHandler.PositionedEvent) {
}, DRAW_SCREEN_SPACE_EVENT_TYPES[drawEntityParams.graphics])

改为联合类型:

type ClickType = Cesium.ScreenSpaceEventHandler.PositionedEvent | Cesium.ScreenSpaceEventHandler.MotionEvent;

this.handler.setInputAction((click: ClickType) {
    if (click instanceof Cesium.ScreenSpaceEventHandler.MotionEvent) {
       // TODO
       return;
     }
}, DRAW_SCREEN_SPACE_EVENT_TYPES[drawEntityParams.graphics])

click instanceof Cesium.ScreenSpaceEventHandler.MotionEvent这行ts提示
'类型“typeof ScreenSpaceEventHandler”上不存在属性“MotionEvent”',

当前完整的代码如下:

import * as Cesium from 'cesium';
import type { DrawEntityParams, StyleConfig, EntityCollect, GraphicalType } from '@/components/Map/draw.d'
import { generateRandomString } from '@/utils/utils';

const DEFAULT_DRAW_IMAGE_ICON = new URL('@/components/Map/components/assets/point.svg', import.meta.url).href;

const DRAW_SCREEN_SPACE_EVENT_TYPES: {
  [key: string]: Cesium.ScreenSpaceEventType;
} = {
  point: Cesium.ScreenSpaceEventType.LEFT_CLICK,
  billboard: Cesium.ScreenSpaceEventType.LEFT_CLICK,
  polygon: Cesium.ScreenSpaceEventType.MOUSE_MOVE,
  polyline: Cesium.ScreenSpaceEventType.MOUSE_MOVE,
  box: Cesium.ScreenSpaceEventType.MOUSE_MOVE,
  rectangle: Cesium.ScreenSpaceEventType.MOUSE_MOVE,
  ellipse: Cesium.ScreenSpaceEventType.MOUSE_MOVE
};

export default class Draw {
  viewer: Cesium.Viewer; // 当前的视图容器提供程序
  config: StyleConfig; // 用于创建实体的颜色配置
  entityCollect: EntityCollect; // 记录已经绘制的实体
  handler: Cesium.ScreenSpaceEventHandler; // 用户交互程序对象
  constructor(viewer: Cesium.Viewer, config: StyleConfig) {
    /** 
     * cesium实例对象
     */
    this.viewer = viewer;
    /**
     * 绘制要素的相关配置
     * 默认配置
     */
    this.config = config || {
      borderColor: Cesium.Color.BLUE,
      borderWidth: 2,
      material: Cesium.Color.GREEN.withAlpha(0.5),
    }
    /**
     * 初始化保存绘制实体的集合
     */
    this.entityCollect = { point: [], rectangle: [], polyline: [], polygon: [], billboard: [], box: [], ellipse: [] }
    // 初始化用户屏幕事件对象
    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas)
  };
  /**
   * 通过屏幕二维坐标获取点击位置得详细信息
   */
  private getPositionInfo(position: Cesium.Cartesian2, graphics?: GraphicalType) {
    // 点击位置笛卡尔坐标
    let cartesian = this.viewer.camera.pickEllipsoid(position, this.viewer.scene.globe.ellipsoid);
    if (!cartesian) return;
    // 笛卡尔转弧度坐标
    let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid, new Cesium.Cartographic())
    // 经度
    let lng = Cesium.Math.toDegrees(cartographic.longitude);
    // 维度
    let lat = Cesium.Math.toDegrees(cartographic.latitude);
    // 海拔高度
    let height = Number(this.viewer.scene.globe.getHeight(cartographic)?.toFixed(2));
    // 实体的唯一标注
    let id = graphics ? `${graphics}_${generateRandomString()}` : generateRandomString();
    return {
      cartesian,
      cartographic,
      lng,
      lat,
      height,
      id
    }
  };
  /**
   * 绘制实体
   * 
   */
  drawEntity(drawEntityParams: DrawEntityParams) {
    const { graphics, _type, callback } = drawEntityParams;
    this.handler.destroy();
    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas)
    this.handler.setInputAction((click: Cesium.ScreenSpaceEventHandler.PositionedEvent) => {
      const positionInfo = this.getPositionInfo(click.position, drawEntityParams.graphics);
      if (!positionInfo) return;
      const { lng, lat, id, height } = positionInfo;
      const entity = this.viewer.entities.add({
        position: Cesium.Cartesian3.fromDegrees(lng, lat, height),
        id,
        billboard: {
          // 图像地址,URI或Canvas的属性
          image: DEFAULT_DRAW_IMAGE_ICON,
          // 设置颜色和透明度
          color: Cesium.Color.WHITE.withAlpha(0.8),
          // 高度(以像素为单位)
          height: 50,
          // 宽度(以像素为单位)
          width: 50,
          // 大小是否以米为单位
          sizeInMeters: false,
          // 相对于坐标的垂直位置
          verticalOrigin: Cesium.VerticalOrigin.CENTER,
          // 相对于坐标的水平位置
          horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
          // 该属性指定标签在屏幕空间中距此标签原点的像素偏移量
          pixelOffset: new Cesium.Cartesian2(0, -12.5),
          // 应用于图像的统一比例。比例大于会1.0放大标签,而比例小于会1.0缩小标签。
          scale: 1.0,
          // 显示在距相机的距离处的属性,多少区间内是可以显示的
          // distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 1500),
          // 是否显示
          show: true,
          heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
          disableDepthTestDistance: Number.POSITIVE_INFINITY
        }
      });
      const entityInfo = { id, position: [lng, lat], entity, graphics, _type };
      this.entityCollect.point.push(entityInfo);
      if (callback) callback(entityInfo)
    }, DRAW_SCREEN_SPACE_EVENT_TYPES[drawEntityParams.graphics])

    this.handler.setInputAction(() => {
      this.handler.destroy();
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
  };
  /**
   * 获取绘制的实体集合
   */
  get getEntityCollect() {
    return this.entityCollect
  }
}

补充:
我按照采纳的回答修改了代码,
然后在setInputAction的回调方法内通过 click instanceof Cesium.ScreenSpaceEventHandler.MotionEvent,来处理两个不同情况,但是ts提示了类型
“typeof ScreenSpaceEventHandler”上不存在属性“MotionEvent”
代码如下:

this.handler.setInputAction((click: ClickType<typeof eventType>) => {
    if (click instanceof Cesium.ScreenSpaceEventHandler.MotionEvent) {
        // TODO
    } else {
        // TODO
    }
}, DRAW_SCREEN_SPACE_EVENT_TYPES[drawEntityParams.graphics])

然后在GPT的帮助下,写了如下方法,一切就都正常了:

function isPositionedEvent(click: ClickType<Cesium.ScreenSpaceEventType>): click is Cesium.ScreenSpaceEventHandler.PositionedEvent {
  return 'position' in click;
}
this.handler.setInputAction((click: ClickType<typeof eventType>) => {
    if (!isPositionedEvent(click)) {
        console.log(click.endPosition)
      } else {
        console.log(click.position)
    }
}, DRAW_SCREEN_SPACE_EVENT_TYPES[drawEntityParams.graphics])

我想知道为什么不能使用click instanceof Cesium.ScreenSpaceEventHandler.MotionEvent?
GPT给我的解决方案是否合理?
我这样写复杂的方式是否合理?

因为完全可以通过以下方式分不同情况编码, 只是多写了几次this.handler.setInputAction,比如:

const graphicsFuns = {
      billboard: () => {
        this.handler.setInputAction((click: Cesium.ScreenSpaceEventHandler.PositionedEvent) => {
          const positionInfo = this.getPositionInfo(click.position, drawEntityParams.graphics);
          if (!positionInfo) return;
          const { lng, lat, id, height } = positionInfo;
          const entity = this.viewer.entities.add({
            position: Cesium.Cartesian3.fromDegrees(lng, lat, height),
            id,
            billboard: {
              // ...
            }
          });
          const entityInfo = { id, position: [lng, lat], entity, graphics, _type };
          this.entityCollect.point.push(entityInfo);
          if (callback) callback(entityInfo)
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
      },
      polyline: () => {
        this.handler.setInputAction((click: Cesium.ScreenSpaceEventHandler.MotionEvent) => {
          const positionInfo = this.getPositionInfo(click.endPosition, drawEntityParams.graphics);
          if (!positionInfo) return;
          const { lng, lat, id, height } = positionInfo;
          const entity = this.viewer.entities.add({
            position: Cesium.Cartesian3.fromDegrees(lng, lat, height),
            id,
            polyline: {
              // ...
            }
          });
          const entityInfo = { id, position: [lng, lat], entity, graphics, _type };
          this.entityCollect.point.push(entityInfo);
          if (callback) callback(entityInfo)
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
      }
    };
阅读 2.7k
1 个回答

写一个映射:

type EventTypeMap = {
  [K in Cesium.ScreenSpaceEventType]: K extends Cesium.ScreenSpaceEventType.LEFT_CLICK
    ? Cesium.ScreenSpaceEventHandler.PositionedEvent
    : Cesium.ScreenSpaceEventHandler.MotionEvent;
};

再写一个类型:

type ClickType<T extends Cesium.ScreenSpaceEventType> = EventTypeMap[T];

最后:

drawEntity(drawEntityParams: DrawEntityParams) {
  const eventType = DRAW_SCREEN_SPACE_EVENT_TYPES[drawEntityParams.graphics];
  this.handler.setInputAction((click: ClickType<typeof eventType>) => {
    // ...
  }, eventType);
  // ...
}

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