当前位置: 首页 > 知识库问答 >
问题:

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

年嘉禧
2023-08-16

以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)      }    };

共有1个答案

怀洛华
2023-08-16

写一个映射:

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);  // ...}
 类似资料:
  • 比如: 我尝试过用‘url’ in opts来判断,但是这样可能会访问到原型链,用opts.url === undefined也会被ts提醒说可能没有url属性。或者用断言来回避这个问题,但是我想知道有没有更好的代码组织方式,让这个问题不发生,而不是回避它

  • 我以为我知道指针,但当我研究运行时多态性/动态绑定时,我看到了指针的一种非常不同的用法。下面是我的3个问题,它们都是关于下面的单行代码的:

  • 问题内容: 我有两个不同的数组,一个带有字符串,另一个带有整数。我想将它们连接成一个数组,其中每一列都具有原始数据类型。我当前执行此操作的解决方案(请参见下文)将整个数组转换为dtype = string,这似乎在内存方面效率很低。 何时和何时可能使dtypes多元化? 问题答案: 一种方法可能是使用记录数组。“列”与标准numpy数组的列不同,但是对于大多数用例来说,这就足够了: 请注意,您还可

  • 问题内容: ./chains.go:26:10:不能在作业中使用UpperCaseHandler(typefunc(asl.MessageDelivery))作为asl.MessageHandler类型./chains.go:37:86:无法使用RepeatHandler(类型func(asl.MessageDelivery))与Repeater.ConsumeFunc的参数中的asl.Messa

  • 为了自定义,我创建了两个函数,它们具有相同的返回类型和不同的参数,一个是单节点配置,另一个是集群配置。守则如下: 调用它们时,返回值(LettuceConnectionFactory)是另一个函数的参数。守则如下: 但是spring会报告 由于项目稍微复杂一些,所以有很多redis单服务器A、B、C。和集群服务器A、B、C...,它们有不同的ip、端口和池策略。我最初的想法是,通过将RedisCo

  • 给定一个依赖于泛型类型参数的结构,我们可以定义一个实现依赖于该类型的关联函数吗? 我想将结构传递给例程,但根据内部类型,让关联的函数以不同的方式计算。这个例程还取决于结构中的成员,所以我宁愿不把所有东西都移到一个特质上。 例如,以下代码尝试根据所涉及的类型定义不同的函数: 这会导致编译器错误: 如果我将的定义转移到另一个特征中,我们就会遇到一个类似但不同的问题。 这会导致编译器错误: 严格来说,我