提问人:Pablo Fonseca 提问时间:11/17/2023 最后编辑:Pablo Fonseca 更新时间:11/19/2023 访问量:42
TypeScript 中动态导入类的 instanceof 检查失败的问题
Issue with instanceof Checks Failing for Dynamically Imported Classes in TypeScript
问:
我正在处理一个 TypeScript 项目,我在其中动态导入 Discord 机器人的类,但我遇到了一个问题,即 instanceof 检查在运行时失败,即使设置中一切看起来都是正确的。
环境:
Node.js 版本:20.9.0
TypeScript 版本:5.2.2
项目设置:使用 discord.js 的 Discord 机器人
问题:正在返回应属于特定类的实例的检查。尽管 TypeScript 编译未显示任何错误,但该问题发生在运行时环境中。instanceof
false
代码概述:我有一个设置,其中 Discord 机器人的命令和事件是从文件中动态加载的。主要类结构如下:
ReadableCommandObject
并且是基类。ReadableEventObject
特定命令,如 .
该方法动态导入这些命令类。Ping extends ReadableCommandObject
ObjectFetcher.read
Ping 命令示例:
下面是一个命令类的示例:
import { type CommandInteraction, SlashCommandBuilder, type CacheType } from 'discord.js';
import ReadableCommandObject from '../../../../utilities/ObjectFetcher/ReadableCommandObject';
class Ping extends ReadableCommandObject {
data: SlashCommandBuilder;
execute: (interaction: CommandInteraction<CacheType>) => Promise<any>;
constructor () {
super();
this.data = new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies pong!');
this.execute = async (interaction: CommandInteraction<CacheType>) => {
return await interaction.reply('Pong!');
};
}
getReadableData (): unknown {
return {
data: this.data,
execute: this.execute
};
};
}
export default Ping;
对象获取方法:
/* eslint-disable @typescript-eslint/no-extraneous-class */
import type IReadableObject from './IReadableObject';
class ObjectFetcher {
static async read (filePath: string): Promise<IReadableObject> {
try {
if (!filePath || filePath.length === 0) {
throw new Error('The file path provided is empty');
}
const readableObjectClass = await require(filePath).default;
const readableObject = new readableObjectClass();
return readableObject;
} catch (error) {
console.log('There was an error while fetching the object: ', error);
throw error;
}
}
}
/* eslint-enable @typescript-eslint/no-extraneous-class */
export default ObjectFetcher;
问题复制:
在运行时,当我尝试检查导入的对象是否是 or 的实例时,检查失败:ReadableCommandObject
ReadableEventObject
// Asynchronously crawls specified directories for command and event JS files, dynamically loading and processing each found file.
const objectFetchResolver = async () => {
const filePaths: string[] = ['core/models/commands', 'core/models/events'];
for (const filePath of filePaths) {
const crawler = new FileCrawler(path.join(__dirname, filePath), '.js', true, true);
const objectFilePaths = await crawler.crawl();
for (const objectFilePath of objectFilePaths) {
const objectInFile = await ObjectFetcher.read(objectFilePath);
if (objectInFile instanceof ReadableCommandObject) {
console.log('Entered in ReadableCommandObject');
commandObjectAdderModerator(objectInFile, objectFilePath);
} else if (objectInFile instanceof ReadableEventObject) {
console.log('Entered in ReadableEventObject');
eventObjectAdderModerator(objectInFile, objectFilePath);
} else {
console.log('Nothing matched');
}
}
};
};
输出:
Nothing matched
Nothing matched
Nothing matched
其他上下文:
有趣的是,当我在调试器中运行相同的代码时(例如,使用 Visual Studio Code 的调试器或 Node.js 的调试器),检查按预期工作,并且我得到了正确的数据。但是,当正常运行应用程序(在调试器外部)时,检查将失败。此行为表明问题可能与环境或执行上下文有关。--inspect
instanceof
instanceof
调试器中的特定行为:
调试时,返回 .
在正常执行中,相同的检查返回 .
问:
是什么原因导致调试器和正常运行时之间的检查行为存在差异?Node.js 执行环境或 TypeScript 的编译过程是否存在特定方面在调试上下文中可能的行为不同?objectInFile instanceof ReadableCommandObject
true
false
instanceof
- 已确保文件路径和导出正确无误。
- 检查并重新编译了 TypeScript 代码。
- 已检查编译的JavaScript代码是否存在任何异常。
- 已验证 Node.js 和 TypeScript 版本。
解决方案更新
在探索了各种潜在的解决方案并考虑了反馈和建议之后,我决定将我的方法从使用 instanceof 转变为利用 TypeScript 的类型保护和接口。此更改更符合 TypeScript 的设计模式,并解决了我面临的问题。
新方法:
TypeScript 类型保护:实现了自定义类型保护,以动态检查对象的类型。此方法提供了一种更可靠且面向 TypeScript 的方法来确保对象的类型正确。
接口和类型检查:使用接口来定义命令和事件对象的结构。这种方法不仅使代码更具可读性和可维护性,而且还利用了 TypeScript 静态类型检查的全部功能。
示例实现:
以下是我如何实现这一点的简短示例:
import { type SlashCommandBuilder, type CommandInteraction } from 'discord.js';
interface ICommand {
type: 'command'
data: SlashCommandBuilder
execute: (interaction: CommandInteraction) => Promise<any>
}
export default ICommand;
import type IReadableObject from '../readable-object';
import type ICommand from '../commands/command';
function isCommand (obj: IReadableObject): obj is ICommand {
return obj.type === 'command';
}
export default isCommand;
// Usage
const loadObjects = async () => {
const filePaths = ['core/models/commands', 'core/models/events'];
for (const file of filePaths) {
const crawler = new FileCrawler(path.join(__dirname, file), '.js', true, true);
const objectFilePaths = await crawler.crawl();
for (const objectFilePath of objectFilePaths) {
const object: ReadableObject = await ObjectFetcher.read(objectFilePath);
readableObjectHandler(object);
}
}
};
// So here I just handle the type of each and accordingly assign the adder moderator
const readableObjectHandler = (readableObject: ReadableObject): void => {
if (isCommand(readableObject)) {
commandObjectAdderModerator(readableObject);
} else if (isEvent(readableObject)) {
eventObjectAdderModerator(readableObject);
}
};
这种方法有效地解决了我在 instanceof 检查中遇到的运行时问题,并且它更自然地融入了 TypeScript 生态系统。
答: 暂无答案
评论