Reputation: 1008
Is there a way in TypeScript to allow one file to access methods in another file, but not make them globally accessible?
The use case for this is having a large class with some private methods that are dangerous and shouldn't be publicly exposed to be potentially used anywhere in the codebase. I want to be able to write the methods that access them in another file to allow logically grouping them without them all being in a single giant class.
Ideally, something like Java's package scope would let me declare that the two files can access each others' dangerous methods but not anyone else. Is there any TypeScript language feature that allows this?
Example:
Class A has methods d1
through d100
that are all dangerous and shouldn't be globally accessible.
Class B has methods s1
through s100
that are safe for public consumption within the project. Each s
method calls through to a d
method after doing some safety checking. Class B needs to have access to all of the d
methods.
Class C wants to call any of the s
methods and should be able to, but shouldn't be able to call any of the d
methods.
However, as I understand Typescript, if I export any of the d
methods so that B can call them, they are then accessible to C as well. If this were Java, I would put A and B in the same package and make the d
methods package scope.
There doesn't seem to be any parallel to this in TypeScript, but is there anything that simulates the goals of 1) being able to break the functionality in to separate files but 2) limiting who can call the methods?
(Yes, I know once it's compiled down to Javascript all bets are off. The goal is to just use TypeScript as a static checker to verify the contracts at compile time.)
Upvotes: 26
Views: 12052
Reputation: 7988
Using a namespace split across files seems like it would accomplish this nicely:
https://www.typescriptlang.org/docs/handbook/namespaces.html#splitting-across-files
As with this in MyNamespace.ts
file:
namespace MyNamespace {
export interface Foo {}
}
And the same namespace plus a reference tag in another file:
/// <reference path="MyNamespace.ts" />
namespace MyNamespace {
export class Bar implements Foo {}
}
Upvotes: 4
Reputation:
There is no Java package scope equivalent in TypeScript, but sometimes it is really needed. This is my solution:
export class Foo {
private doSomething(): void {
//do something
}
}
interface FooUnlocker {
doSomething(): void;
}
const foo: Foo = new Foo();
(<FooUnlocker><any>foo).doSomething();
Advantages - there is need in extra convention, like _doSomething() etc, code is clean and we have compilation check.
Disadvantages - we need to sync Foo and FooUnlocker.
Node, that FooUnlocker is not exported, so it is visible only within module (if ES6 modules are used).
Upvotes: 0
Reputation: 28757
There are no direct ways to enforce this in Typescript. In my experience, the best way to solve this sort of problem is through clear documentation and clear naming patterns.
If there is a unskilled or malicious dev, they will have any number of ways to create havoc and compiler limitations will do little to stop them. However, all reasonably skilled and ethical developers should be able to avoid calling internal, dangerous methods.
My recommendation would be to generate some sort of naming convention. All non-API methods/properties/fields that are public due to language limitations should have the same prefix or suffix.
For example, the way that our team works is the following:
_
.Internal
.internal
folder. Eg- src/models
has the models modules that are API. src/models/internal
has the models modules that are not API.Also, we have not done this yet, but are considering creating tslint rules to help enforce these rules. We haven't gone too far down this path yet since we haven't had any devs accidentally using non-API, but this is still a possibility.
In my opinion, proper naming convention, proper document, and proper mentorship is enough to solve this problem, which I agree is a limitation of the Typescript language.
Upvotes: 19
Reputation: 1298
I would hide the dangerous methods using the approach proposed by Rob: put class A in the same file as class B. Export class B, but don't export class A. This way, class A is only visible for class B. Of course, as an alternative, you could create a giant class instead, but you are right to hesitate about that path...
You should take care though: TypeScript compiles to JavaScript, therefore every bit of unsafe code is available for unwanted usage from a JavaScript perspective.
Upvotes: 0