【问题标题】:Typescript Equivalent of package scope?打字稿等效于包范围?
【发布时间】:2017-05-15 05:12:17
【问题描述】:

TypeScript 中有没有一种方法可以允许一个文件访问另一个文件中的方法,但不能让它们全局访问?

这个用例是有一个带有一些私有方法的大型类,这些方法很危险,不应该公开暴露在代码库的任何地方可能使用。我希望能够在另一个文件中编写访问它们的方法,以允许在逻辑上对它们进行分组,而不会将它们全部放在一个巨大的类中。

理想情况下,像 Java 的包范围这样的东西可以让我声明这两个文件可以访问彼此的危险方法,但不能访问其他任何人。是否有任何允许这样做的 TypeScript 语言功能?

例子:

A 类具有 d1d100 的方法,这些方法都是危险的,不应在全局范围内访问。

B 类具有 s1s100 的方法,这些方法对于项目内的公众使用是安全的。每个s 方法在进行一些安全检查后调用d 方法。 B 类需要能够访问所有 d 方法。

C 类想要调用任何s 方法并且应该能够但不应该能够调用任何d 方法。

但是,据我了解 Typescript,如果我导出任何 d 方法以便 B 可以调用它们,那么 C 也可以访问它们。如果这是 Java,我会将 A 和 B 放在同一个包中,并设置 d 方法包范围。

TypeScript 中似乎没有与此类似的东西,但有什么可以模拟以下目标:1)能够将功能分解到单独的文件中,但 2)限制谁可以调用这些方法?

(是的,我知道一旦它被编译成 Javascript,所有的赌注都没有了。我们的目标是只使用 TypeScript 作为静态检查器来在编译时验证合约。)

【问题讨论】:

  • 为什么不把A类和B类放在同一个文件中,并且不导出A?
  • @Rob 现在就是这样。这两个类都在变大,最简单的重构就是将它们分成两个文件。
  • 丑陋的部分解决方法,使用/** @internal **/ 隐藏.d.ts 文件中的内容。虽然有an open proposal,但似乎还没有什么好的方法可以做到这一点
  • 我在其他语言中看到的一种风格是将私有内容放在名称为mypkg/internalmypkg/private 的“内部”模块中。虽然内部模块在技术上仍然是公开的,但很明显“不要导入这个!”图书馆客户的信号
  • 如果你没有用 export 标记一个类,并在模块中声明它,在与使用它的类相同的文件中,tsc 编译。将类放在另一个文件中,它找不到它。

标签: typescript typescript2.0


【解决方案1】:

在 Typescript 中没有直接的方法来强制执行此操作。根据我的经验,解决此类问题的最佳方法是通过清晰的文档和清晰的命名模式。

如果有不熟练或恶意的开发人员,他们将有多种方式来制造破坏,编译器限制将无助于阻止他们。但是,所有具有相当技能和道德的开发人员都应该能够避免调用内部的危险方法。

我的建议是生成某种命名约定。由于语言限制而公开的所有非 API 方法/属性/字段应具有相同的前缀或后缀。

例如,我们团队的工作方式如下:

  1. 所有非 API 公共方法/属性/字段都以 _ 为前缀。
  2. 所有非 API 公共类都以Internal 为后缀。
  3. 所有非 API 模块都在 internal 文件夹中。例如-src/models 具有 API 模型模块。 src/models/internal 的模型模块不是 API。
  4. 所有非 API 类都有注释,将它们记录为非 API。

此外,我们还没有这样做,但正在考虑创建 tslint 规则来帮助执行这些规则。我们还没有在这条路上走得太远,因为我们没有任何开发人员意外使用非 API,但这仍有可能。

在我看来,适当的命名约定、适当的文档和适当的指导足以解决这个问题,我同意这是 Typescript 语言的限制。

【讨论】:

  • 我认为主要问题在于开发人员没有意识到他们正在进入他们不应该进入的地方。也许最小的解决方案只是在每个应该具有受限范围的外部可访问标识符之前添加前缀 _。该前缀告诉开发人员他们需要在使用前仔细检查预期范围。
【解决方案2】:

使用跨文件拆分的命名空间似乎可以很好地完成此任务:
https://www.typescriptlang.org/docs/handbook/namespaces.html#splitting-across-files

MyNamespace.ts 文件中的这个一样:

namespace MyNamespace {
    export interface Foo {}
}

并且同一个命名空间加上另一个文件中的引用标签:

/// <reference path="MyNamespace.ts" />
namespace MyNamespace {
    export class Bar implements Foo {}
}

【讨论】:

【解决方案3】:

我会使用 Rob 提出的方法隐藏危险的方法:将 A 类与 B 类放在同一个文件中。导出 B 类,但不要导出 A 类。这样,A 类只对 B 类可见. 当然,作为替代方案,你可以创建一个巨大的类,但你对这条路犹豫是对的......

但您应该小心:TypeScript 编译为 JavaScript,因此从 JavaScript 的角度来看,每一个不安全的代码都可用于不必要的使用。

【讨论】:

    【解决方案4】:

    TypeScript 中没有等效的 Java 包范围,但有时确实需要它。这是我的解决方案:

    export class Foo {
    
        private doSomething(): void {
        //do something
        }
    }
    
    interface FooUnlocker {
    
       doSomething(): void;
    }
    
    const foo: Foo = new Foo();
    (<FooUnlocker><any>foo).doSomething();
    

    优点 - 需要额外的约定,如 _doSomething() 等,代码干净,我们有编译检查。

    缺点 - 我们需要同步 Foo 和 FooUnlocker。

    节点,FooUnlocker 没有被导出,所以它只在模块内可见(如果使用 ES6 模块)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-08-09
      • 2021-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多