【问题标题】:Typescript - Execute code in files without importing themTypescript - 在文件中执行代码而不导入它们
【发布时间】:2019-01-01 17:37:00
【问题描述】:

有什么方法可以在文件中运行代码而不将它们导入 TypeScript(Angular 项目)?也许通过 tsconfig 中的一些配置指定文件模式?

我想做的是将一些类注册到一个全局对象中,但我不想在单个文件中维护类列表。我宁愿在每个类定义下都有一行简单的代码,如下所示:

export class MyClassToRegister {...}

GlobalRegistry.register(MyClassToRegister);

我知道导入文件时会执行此代码,但有时为时已晚。有什么方法可以实现的吗?

【问题讨论】:

  • 你在使用 Webpack 来打包/构建你的 TypeScript 项目吗?
  • 是的,我是。这是一个基本的 Angualr 5 项目

标签: angular typescript


【解决方案1】:

是的,通过使用 Webpack 的 require.context(...),您可以将文件目录导入到您的包中。

快速说明:您仍在将文件导入包中,但您不必静态定义每个导入路径,或者在添加/删除文件时手动保持它们是最新的。

文件结构:

让我们使用这个示例文件结构:

src/
    items/
        item1.ts
        item2.ts
        item3.ts
    registry.ts
    index.ts

以下是我们需要从目录中获取的项目:

//item1.ts, item2.ts, item3.ts
import GlobalRegistry from "../registry";

export class Item1 {
    //...
}
GlobalRegistry.register(Item1);

加载的项目将在此服务(或任何您的业务逻辑)中注册自己 - 这证明项目正在加载:

//registry.ts
export default class GlobalRegistry {

    static _items = [];

    static register(cls){
        console.log('Register class: ', cls.name);
        this._items.push(cls.name);
    }

    static getItems(){
        return this._items;
    }

}

require.context(...)

使用require.context(...) 要求'items'目录下的所有文件:

//index.ts
import GlobalRegistry from './registry';

// Import all files under './items/*.ts'
var context = require.context('./items', true, /\.ts$/);
context.keys().forEach((key) => {
    context(key);
});

console.log('Loaded classes:', GlobalRegistry.getItems());

最后,为了让 TypeScript 满意,我们声明了 Webpack 提供的 require.context() 接口:

//references.d.ts

// Extend @types/node NodeRequire type to define Webpack's context function
declare interface NodeRequire {
    context: (dir: string, includeSubdirs: boolean, filter: RegExp) => any;
}

// Tell TypeScript that there is a global `require` variable available to us
declare var require: NodeRequire;

结果:

当应用程序运行时,您应该会看到已注销:

Register class:  Item1
Register class:  Item2
Register class:  Item3
Loaded classes: (3) ["Item1", "Item2", "Item3"]

注意事项:

1.包含顺序 如果您首先引用单个类,则不保证包含的顺序。

例如,如果您显式导入一个类型并将其用作值,则该类型将在其他仅通过require.context(...) 包含的类型之前加载。

示例 - 使用 Item2 作为值:

//index.ts

/* require.context(...) stuff is here */

import {Item2} from './items/Item2';
let myItem = new Item2();   // use Item2 as a value

改变加载顺序:

Register class:  Item2
Register class:  Item1
Register class:  Item3
Loaded classes: (3) ["Item2", "Item1", "Item3"]

但请注意,仅按类型(而不是按值)引用不会改变加载顺序

let myItem: Item2;  // just referencing by type won't affect load order

2。需要函数依赖警告

您可能会在构建过程中收到警告,例如:require function is used in a way in which dependencies cannot be statically extracted

这不一定有什么错误,只是 Webpack 让你知道你正在用 require 做一些时髦的事情 - 这是真的 :)

执行此动态 require 可能会影响您的 bundle 的 tree-shaking 或其他静态分析(即,如果不使用这些类型,则不能从 bundle 中排除)。但这可能是一个公平的权衡,因为不必手动管理文件导入 - 您必须根据自己的需求/项目目标进行评估。

【讨论】:

  • 听起来完全符合我的需要,我明天试试。谢谢
  • 酷,报告你的结果 - 我很想看看它在你的项目中的结果:)
  • “这不一定有什么问题,只是 Webpack 让你知道你正在用 require 做一些时髦的事情——这是真的 :)” 这说明了一个重要的事情:“我' m 无法以静态方式解决路径中的依赖关系”。如果你在变量中给出路径,它实际上不起作用,因为它不会搜索相对于当前路径的依赖关系。
【解决方案2】:

一种解决方案是使用multi-file namespaces,它允许将代码拆分为多个文件,而无需导入modules

这是一个来自TypeScript documentation on namespaces的简化示例。

Validation.ts 文件

namespace Validation {
    export interface StringValidator {
        isAcceptable(s: string): boolean;
    }
}

LettersOnlyValidator.ts 文件

namespace Validation {
    const lettersRegexp = /^[A-Za-z]+$/;
    export class LettersOnlyValidator implements StringValidator {
        isAcceptable(s: string) {
            return lettersRegexp.test(s);
        }
    }
}

【讨论】:

  • 谢谢,但您似乎必须使用/// <reference> 标签来跟踪文件...所以我看不出这与导入语句之间的区别。
  • 三斜杠指令是可选的。假设这两个文件都包含在您的 tsconfig.json 选项中(例如 "include": ["**/*.ts"]),则从上面的示例中删除该行仍然编译为例外。
  • 哦,太好了!那我今天就测试一下!
  • 说找不到StringValidator
猜你喜欢
  • 1970-01-01
  • 2011-04-14
  • 2014-06-07
  • 1970-01-01
  • 1970-01-01
  • 2021-07-15
  • 2016-09-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多