You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在TypeScript中映射类型并添加基于this的类型守卫?

解决方案

一、实现ImguiContext的类型守卫方法

类体中无法直接使用映射类型定义成员,需先通过类型别名生成方法签名集合,再让类实现该类型,同时利用this类型守卫关联方法调用后的上下文类型。

1. 定义方法签名类型

基于Elements接口生成所有操作方法的类型:

interface Elements extends Record<string, [any[], any]> {
  // 预留接口合并扩展位
}

// 示例扩展
interface Elements {
  button: [[text: string], MouseEvent];
  input: [[value: string], InputEvent]; // 新增扩展示例
}

// 生成所有元素方法的类型签名
type ElementMethods = {
  [E in keyof Elements]: (...args: Elements[E][0]) => this is ImguiContext<E>;
};

2. 实现ImguiContext类

让类实现ElementMethods,并正确处理泛型参数与event属性:

class ImguiContext<CurrentEv extends keyof Elements = never> implements ElementMethods {
  // 实现方法(运行时逻辑需自行补充)
  button(text: string): this is ImguiContext<"button"> {
    // 此处编写实际的按钮渲染/事件判断逻辑,返回true表示触发该事件
    return true;
  }

  input(value: string): this is ImguiContext<"input"> {
    return true;
  }

  // 初始状态event为never,调用方法后会被缩小为对应事件类型
  event: CurrentEv extends keyof Elements ? Elements[CurrentEv][1] : never;
}

3. 使用示例

调用方法后,TypeScript会自动推断event的类型:

function main(ctx: ImguiContext) {
  if (ctx.button("Click me")) {
    // ctx.event 被推断为 MouseEvent
    console.log("Clicked at X:", ctx.event.screenX);
  }

  if (ctx.input("Test")) {
    // ctx.event 被推断为 InputEvent
    console.log("Input value:", ctx.event.target.value);
  }
}

4. 接口合并扩展支持

后续只需合并Elements接口,再补充类的方法实现即可:

// 扩展Elements
declare module "./your-module-path" {
  interface Elements {
    slider: [[min: number, max: number], Event];
  }
}

// 给ImguiContext补充方法
ImguiContext.prototype.slider = function(min: number, max: number): this is ImguiContext<"slider"> {
  return true;
};

二、无参类型守卫/断言函数的问题解析

你尝试的aIsNumber函数无法直接作用于外部变量a,原因如下:

  • 类型谓词(a is number):必须作用于函数参数,用于判断传入参数的类型,而非外部作用域变量。
  • 断言函数(asserts condition):需关联函数参数或类的this,无法直接断言外部变量类型。

解决方式

若要判断外部变量类型,有两种可行方案:

1. 将变量作为参数传入

let a: string | number = 1;

// 类型谓词版本
function isNumber(val: string | number): val is number {
  return typeof val === "number";
}

if (isNumber(a)) {
  // a 被推断为 number
  console.log(a.toFixed(2));
}

2. 使用断言函数结合类型断言(不推荐,仅适用于明确场景)

若必须无参,可通过类型断言强制缩小类型,但会丢失类型检查安全性:

function assertAIsNumber(): asserts a is number {
  if (typeof a !== "number") {
    throw new Error("a is not a number");
  }
}

assertAIsNumber();
// 此时a被断言为number
console.log(a.toFixed(2));

注意:这种方式依赖函数内部对外部变量的访问,TypeScript类型系统无法跟踪该关联,仅在函数执行后强制缩小类型,存在运行时风险。


内容的提问来源于stack exchange,提问作者KermanX

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 01:23:33