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
相关产品推荐
相关产品推荐

