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

Telegraf与TypeScript场景切换时ctx.session属性类型错误解决

问题解决:Telegraf场景中Session自定义字段类型错误

错误原因

你遇到的类型错误是因为Telegraf的SceneSession结构里,自定义会话数据并不是直接挂在session根对象下,而是存放在session.data属性中。另外你自定义的MyContext重复声明了session和scene属性,覆盖了Scenes.SceneContext提供的正确类型定义,导致TS无法识别你扩展的MySessionData字段。

修复步骤

1. 修正上下文类型定义

删掉MyContext里重复的session和scene声明,直接继承Scenes.SceneContext<MySessionData>即可,它已经包含了正确的会话和场景类型:

interface MyContext extends Scenes.SceneContext<MySessionData> {}

2. 正确访问自定义会话数据

所有自定义字段都要通过ctx.session.data来访问,而不是直接用ctx.session:

// 原错误写法
// ctx.session.examId = parseInt(ctx.match[1], 10);

// 修正后
ctx.session.data.examId = parseInt(ctx.match[1], 10);
await ctx.reply(`You selected exam ID: ${ctx.session.data.examId}`);

完整修复后的代码

import { Telegraf, Scenes, Markup } from 'telegraf';

// Define scene constants
const SCENES = {
  EXAMS: 'exams-scene',
  YEARS: 'years-scene',
} as const;

// Extend SceneSessionData to include custom fields
interface MySessionData extends Scenes.SceneSessionData {
  examId?: number;
  year?: string;
  subject?: string;
  paper?: string;
  currentPage?: number;
  lastActivity?: number;
}

// 修正上下文类型,无需重复声明session和scene
interface MyContext extends Scenes.SceneContext<MySessionData> {}

// Initialize scenes
const examsScene = new Scenes.BaseScene<MyContext>(SCENES.EXAMS);
const yearsScene = new Scenes.BaseScene<MyContext>(SCENES.YEARS);

// Exam selection scene
examsScene.enter(async (ctx) => {
  const exams = [
    { id: 1, title: 'GCE O-Level' },
    { id: 2, title: 'GCE A-Level, BACALUREATE' },
    { id: 3, title: 'IGCSE' },
    { id: 4, title: 'SAT' },
  ];

  const keyboard = Markup.inlineKeyboard([
    ...exams.map((exam) => [
      Markup.button.callback(exam.title, `select_exam:${exam.id}`),
    ]),
    [Markup.button.callback('« Main Menu', 'main_menu')],
  ]);

  await ctx.reply('Select an exam:', keyboard);
});

// Handle exam selection
examsScene.action(/^select_exam:(\d+)$/, async (ctx) => {
  // 通过session.data访问自定义字段
  ctx.session.data.examId = parseInt(ctx.match[1], 10);
  await ctx.reply(`You selected exam ID: ${ctx.session.data.examId}`);
  await ctx.scene.enter(SCENES.YEARS); // Proceed to the next scene
});

// 注册场景管理器到bot(不要遗漏)
const bot = new Telegraf<MyContext>('YOUR_BOT_TOKEN');
const stage = new Scenes.Stage<MyContext>([examsScene, yearsScene]);
bot.use(session());
bot.use(stage.middleware());

bot.launch();

额外说明

  • 必须正确配置Telegraf的session中间件,否则session.data会无法正常读写。
  • 所有需要跨场景共享的自定义数据,都要放在MySessionData接口里,统一通过session.data访问。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 19:17:35