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

将独立注入式JS脚本迁移到TypeScript时return语句编译报错的问题咨询

将独立注入式JS脚本迁移到TypeScript时return语句编译报错的问题咨询

问题描述

我正在将一批独立的注入式JavaScript脚本迁移到TypeScript,使用esbuild编译TS文件生成对应的JS脚本。这些脚本注入到页面时会被包装成AsyncFunction执行,所以原JS脚本中直接使用return语句来返回执行结果是合法的。但在TypeScript文件中直接写return时,编译器会报错:A 'return' statement can only be used within a function body.ts(1108)。

举个简单的例子,我的TS文件内容如下:

const input = document.querySelector('someSelector');
input.value = "someValue";

return true;

而注入脚本的代码逻辑是:

const scriptToInject = loadScript('someScript');
const globalVars = {};

const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
const func = new AsyncFunction(globalVars, codeToRun);
const codeResult = await func(globalVars );

问题根源

TypeScript的静态类型检查会严格校验return语句的位置:它认为你的TS文件是全局脚本或ES模块,在这两种上下文的顶级作用域中,return语句是不允许的。但你的脚本实际运行时,会被动态包装进AsyncFunction的函数体内执行,这种运行时的上下文TS编译器无法感知到,因此产生了报错。

解决方案

针对这个问题,我提供几个从简单到规范的解决方案,你可以根据脚本的复杂度选择:

方案1:使用// @ts-expect-error注释忽略单个错误(推荐小型脚本)

这是最直接的解决方式:在return语句上方添加// @ts-expect-error注释,明确告诉TS编译器你预期这里会有一个错误,仅忽略这一行的校验。这种方式不会影响其他代码的类型检查,也不需要修改构建流程。

示例TS文件:

// 处理document.querySelector可能返回null的情况,避免TS报错
const input = document.querySelector('someSelector');
if (!input) {
  console.error("目标输入框未找到");
  // @ts-expect-error: 运行时脚本会被包装进AsyncFunction,return合法
  return false;
}

input.value = "someValue";

// @ts-expect-error: 运行时脚本会被包装进AsyncFunction,return合法
return true;

方案2:包装为函数+构建时提取函数体(推荐大型/复杂脚本)

对于逻辑复杂的脚本,更规范的做法是将代码包裹在一个函数中(让TS编译器通过校验),然后通过构建工具提取函数体的内容,最终输出的JS脚本仍然保持原有的“无函数包装”形式。

步骤如下:

  1. 将TS脚本内容放在函数中(支持async函数,适配你的AsyncFunction注入逻辑):
// script.ts
async function injectedScript() {
  const input = document.querySelector('someSelector');
  if (!input) {
    console.error("目标输入框未找到");
    return false;
  }
  input.value = "someValue";
  // 可以添加await逻辑,适配AsyncFunction
  await new Promise(resolve => setTimeout(resolve, 100));
  return true;
}
  1. 配置esbuild编译TS文件后,用一个简单的Node.js脚本提取函数体:
    比如编写一个extract-function-body.js:
const fs = require('fs');
const path = require('path');

const inputPath = path.join(__dirname, 'dist/script.js');
const outputPath = path.join(__dirname, 'dist/injectable-script.js');

const code = fs.readFileSync(inputPath, 'utf8');
// 匹配函数体内容(适配async函数)
const match = code.match(/async function injectedScript\s*\{\s*([\s\S]*)\s*\}/) || code.match(/function injectedScript\s*\{\s*([\s\S]*)\s*\}/);
if (match) {
  fs.writeFileSync(outputPath, match[1].trim(), 'utf8');
  console.log('函数体提取完成,输出文件:', outputPath);
} else {
  console.error('无法匹配函数体,请检查输入文件');
}
  1. 在构建流程中,先执行esbuild编译TS,再运行这个提取脚本,最终得到的injectable-script.js就是可以直接注入的内容。

方案3:使用// @ts-ignore注释(不推荐)

和@ts-expect-error类似,但@ts-ignore会忽略注释行之后的所有错误,风险更高,可能会掩盖其他潜在的类型问题,因此不建议使用。

示例:

const input = document.querySelector('someSelector');
input.value = "someValue";

// @ts-ignore
return true;

额外的类型优化建议

为了让TS脚本更健壮,你还可以:

  • 对document.querySelector的返回值做非空校验(如方案1中的if (!input)),或者使用非空断言!(仅当你确定元素一定存在时);
  • 为DOM元素添加类型断言,比如document.querySelector('input') as HTMLInputElement,这样TS会自动提示value等属性,避免类型错误。

示例:

const input = document.querySelector('input#search') as HTMLInputElement;
if (!input) {
  // @ts-expect-error
  return false;
}
input.value = "TypeScript注入脚本";

// @ts-expect-error
return true;

备注:内容来源于stack exchange,提问作者starsoccer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 20:14:30