As in the title: does TypeScript support namespaces? If so, how do I use them?
Typescript allows to define modules closely related to what will be in ECMAScript 6. The following example is taken from the spec:
module outer {
var local = 1;
export var a = local;
export module inner {
export var x = 10;
}
}
As you can see, modules have names and can be nested. If you use dots in module names, typescript will compile this to nested modules as follows:
module A.B.C {
export var x = 1;
}
This is equal to
module A {
module B {
module C {
export var x = 1;
}
}
}
What's also important is that if you reuse the exact same module name in one typescript program, the code will belong to the same module. Hence, you can use nested modules to implement hierarchichal namespaces.
As of version 1.5, Typescript supports namespace keyword. Namespaces are equivalent to internal modules.
From What's new in Typescript:
Before:
module Math {
export function add(x, y) { ... }
}
After:
namespace Math {
export function add(x, y) { ... }
}
For defining an internal module, now you can use both module and namespace.
Here is a TypeScript namespace example:
///<reference path='AnotherNamespace/ClassOne.ts'/>
///<reference path='AnotherNamespace/ClassTwo.ts'/>
module MyNamespace
{
import ClassOne = AnotherNamespace.ClassOne;
import ClassTwo = AnotherNamespace.ClassTwo;
export class Main
{
private _classOne:ClassOne;
private _classTwo:ClassTwo;
constructor()
{
this._classOne = new ClassOne();
this._classTwo = new ClassTwo();
}
}
}
There is no 'namespace' keyword, but internal modules (using the 'module' keyword) and external modules (using the 'export' keyword) offer a similar way to partition your code into logical hierarchies.
False...
module A.B.C {
export var x = 1;
}
is equal to
module A {
export module B {
export module C {
export var x = 1;
}
}
}
because you can write outside the module A :
var y = A.B.C.x;
But :
module A {
module B {
module C {
export var x = 1;
}
var y = C.x; // OK
}
//var y = B.C.x; // Invalid
}
//var y = A.B.C.x; // Invalid
Related
Is it possible to clone a JSON-generated object or string into a Typescript class which I created? We are building a model of our API using Typescript classes. There’s a base class which they all extend which has common/helper methods. When we do JSON.parse(response) to auto-generate objects it creates simple objects and not our custom objects.
Is there a way we can convert those JSON-generated objects into our custom objects, so long as the field names match up? And, to make things more robust, can this but done where our custom objects’ fields are other custom objects and/or arrays of them?
Here is our code, with comments of what we’d like to achieve.
base-model.ts
export class BaseModelObject {
uuid: string; // All of our objects in our model and JSON have this required field populated
matchUUIDs<T extends BaseModelObject>( obj: T): boolean {
return obj.uuid == this.uuid;
}
}
child-model.ts
import { BaseModelObject } from 'base-model';
export class Child extends BaseModelObject {
}
parent-model.ts
import { BaseModelObject } from 'base-model';
import { Child } from 'child-model';
export class Parent extends BaseModelObject {
children: Child[];
}
JSON payload
{
'uuid': '0632a35c-e7dd-40a8-b5f4-f571a8359c1a',
'children': [
{
'uuid': 'd738c408-4ae9-430d-a64d-ba3f085175fc'
},
{
'uuid': '44d56a0d-ad2d-4e85-b5d1-da4371fc0e5f'
}
]
}
In our components and directives and such, we hope to use the helper function in BaseModelObject:
Component code
let parent: Parent = JSON.parse(response);
console.log(parent.uuid); // Works! 0632a35c-e7dd-40a8-b5f4-f571a8359c1a
// Want this to print ‘true’, but instead we get TypeError: parebt.matchUUID is not a function
console.log(parent.matchUUID(‘0632a35c-e7dd-40a8-b5f4-f571a8359c1a’));
// Want this to print ‘true’, but instead we get TypeError: parent.children[0].matchUUID is not a function
console.log(parent.children[0].matchUUID(‘d738c408-4ae9-430d-a64d-ba3f085175fc’));
The problem is that JSON.parse() is not creating our classes, it’s creating simple objects with key/value pairs. So we’re thinking of “cloning” the JSON-generated object into an instance of our class, like this:
base-model.ts
export class BaseModelObject {
[key: string]: any;
matchUUIDs<T extends BaseModelObject>( obj: T): boolean {
return obj['uuid'] == this['uuid'];
}
cloneFields(obj: any) {
for (let prop in obj) {
this[prop] = obj[prop];
}
}
}
Component code
let parent: Parent = new Parent(); // Creates instance of our class
parent.cloneFields(JSON.parse(response)); // Copy JSON fields to our object
console.log(parent.matchUUID('0632a35c-e7dd-40a8-b5f4-f571a8359c1a')); // prints 'true'
console.log(parent.children[0].matchUUID('d738c408-4ae9-430d-a64d-ba3f085175fc')); // Still throws TypeError: parent.children[0].matchUUID is not a function
The problem now rests in the fact that the cloning of the Parent object did not recursively clone the JSON-generated Child objects into instances of our custom Child class.
Since our Parent object is typed at compile-time and it knows that the data type of the children array is Child[] (our custom class), is there a way to use reflection to instantiate the right class?
Our logic would need to say:
Create an instance of our custom class
Tell our instance to clone the fields from the JSON-generated object
Iterate over the fields in the JSON-generated object
For each field name from the JSON-generated object, find the "type definition" in our custom class
If the type definition is not a primitive or native Typescript type, then instantiate a new instance of that "type" and then clone it's fields.
(and it would need to recursively traverse the whole JSON object structure to match up all other custom classes/objects we add to our model).
So something like:
cloneFields(obj: any) {
for (let prop in obj) {
let A: any = ...find the data type of prop...
if(...A is a primitive type ...) {
this[prop] = obj[prop];
} else {
// Yes, I know this code won't compile.
// Just trying to illustrate how to instantiate
let B: <T extends BaseModelUtil> = ...instantiate an instance of A...
B.cloneFields(prop);
A[prop] = B;
}
}
}
Is it possible to reflect a data type from a class variable definition and then instantiate it at runtime?
Or if I'm going down an ugly rabbit hole to which you know a different solution, I'd love to hear it. We simply want to build our custom objects from a JSON payload without needing to hand-code the same patterns over and over since we expect our model to grow into dozens of objects and hundreds of fields.
Thanks in advance!
Michael
There are several ways to do that, but some requires more work and maintenance than others.
1. Simple, a lot of work
Make your cloneFields abstract and implement it in each class.
export abstract class BaseModelObject {
uuid: string;
matchUUIDs<T extends BaseModelObject>( obj: T): boolean {
return obj.uuid == this.uuid;
}
abstract cloneFields(obj: any);
}
class Parent extends BaseModelObject {
children: Child[];
cloneFields(obj: any) {
this.children = obj.children?.map(child => {
const c = new Children();
c.cloneFields(child);
return c;
});
}
}
2. Simple, hacky way
If there is no polymorphism like:
class Parent extends BaseModelObject {
children: Child[] = [ new Child(), new ChildOfChild(), new SomeOtherChild() ]
}
Property names mapped to types.
const Map = {
children: Child,
parent: Parent,
default: BaseModelObject
}
export class BaseModelObject {
uuid: string;
matchUUIDs<T extends BaseModelObject>( obj: T): boolean {
return obj.uuid == this.uuid;
}
cloneFields(obj: any) {
for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
this[prop] = obj[prop]?.map(child => { // You have to check it is an array or not,..
const c = new (Map[prop])();
c.cloneFields(child);
return c;
});
}
}
}
}
You can serialize hints into that JSON. Eg. property type with source/target type name and use it to resolve right types.
3. Reflection
Try tst-reflect. It is pretty advanced Reflection system for TypeScript (using custom typescript transformer plugin).
I'm not going to write example, it would be too complex and it depends on your needs.
You can use tst-reflect to list type's properties and get their types. So you'll be able to validace parsed data too.
Just some showcase from its README:
import { getType } from "tst-reflect";
function printTypeProperties<TType>()
{
const type = getType<TType>(); // <<== get type of generic TType ;)
console.log(type.getProperties().map(prop => prop.name + ": " + prop.type.name).join("\n"));
}
interface SomeType {
foo: string;
bar: number;
baz: Date;
}
printTypeProperties<SomeType>();
// or direct
getType<SomeType>().getProperties();
EDIT:
I created a package ng-custom-transformers that simplifies this a lot. Follow its README.
DEMO
EDIT old:
Usage with Angular
Angular has no direct support of custom transformers/plugins. There is a feature request in the Angular Github repo.
But there is a workaround.
You have to add ngx-build-plus. Run ng add ngx-build-plus.
That package defines "plugins".
Plugins allow you to provide some custom code that modifies your webpack configuration.
So you can create plugin and extend Angular's webpack configuration. But here comes the sun problem. There is no public way to add the transformer. There were AngularCompilerPlugin in webpack configuration (#ngtools/webpack) which had private _transformers property. It was possible to add a transformer into that array property. But AngularCompilerPlugin has been replaced by AngularWebpackPlugin which has no such property. But is is possible to override method of AngularWebpackPlugin and add a transformer there. Getting an instance of the AngularWebpackPlugin is possible thanks to ngx-build-plus's plugins.
Code of the plugin
const {AngularWebpackPlugin} = require("#ngtools/webpack");
const tstReflectTransform = require("tst-reflect-transformer").default;
module.exports.default = {
pre() {},
post() {},
config(cfg) {
// Find the AngularWebpackPlugin in the webpack configuration; angular > 12
const angularWebpackPlugin = cfg.plugins.find((plugin) => plugin instanceof AngularWebpackPlugin);
if (!angularWebpackPlugin) {
console.error("Could not inject the typescript transformer: AngularWebpackPlugin not found");
return;
}
addTransformerToAngularWebpackPlugin(angularWebpackPlugin, transformer);
return cfg;
},
};
function transformer(builderProgram) {
return tstReflectTransform(builderProgram.getProgram());
}
function addTransformerToAngularWebpackPlugin(plugin, transformer) {
const originalCreateFileEmitter = plugin.createFileEmitter; // private method
plugin.createFileEmitter = function (programBuilder, transformers, getExtraDependencies, onAfterEmit, ...rest) {
if (!transformers) {
transformers = {};
}
if (!transformers.before) {
transformers = {before: []};
}
transformers.before = [transformer(programBuilder), ...transformers.before];
return originalCreateFileEmitter.apply(plugin, [programBuilder, transformers, getExtraDependencies, onAfterEmit, ...rest]);
};
}
Then it is required to execute ng commands (such as serve or build) with --plugin path/to/the/plugin.js.
I've made working StackBlitz demo.
Resources I've used while preparing the Angular demo:
https://indepth.dev/posts/1045/having-fun-with-angular-and-typescript-transformers
https://medium.com/#morrys/custom-typescript-transformers-with-angular-for-angular-11-12-and-13-40cbdc9cca7b
Im working in node.js with typescript and I defined the Tariff and Tariffs classes. I have also fake data created with JSON that should be compatible with the Classes. But I'm getting an error in resolve() method that:
Argument of type '{ blabbla... ' is not assignable to parameter of
type 'Tariffs | PromiseLike'.
export class FakeDataProvider implements IDataProvider {
loadTariffs?(request: LoadTariffsRequest): Promise<Tariffs>{
return new Promise<Tariffs>((resolve, reject) => {
resolve(fakeTariffs);
});
}
}
Next I have defined and exported the classes in another file:
export class Tariff {
tariffOptionId: number = 0;
name: string = '';
}
export class Tariffs {
// tariff: Tariff = new Tariff(); // this does not work
tariff: Array<Tariff> = []; // this does not work too
}
Then I have exported fake mock-up JSON data in another file:
let fakeTariffs =
{
'tariffs': {
'tariff': [
{ "name": "tariff1", "tariffOptionId": 1 },
{ "name": "tariff2", "tariffOptionId": 2 },
{ "name": "tariff3", "tariffOptionId": 3 }
]
}
};
export default fakeTariffs;
What I'm doing wrong and how could I modify the classes in order to be compatible with fake data?
You have;
tarrif
in the first part of your code, and;
tariff
in the JSON...
The solution is to use resolve(fakeData.tariffs) instead of resolve(fakeData).
Are there any examples around using this ico with ES6 rather than Typescript for back-end Node/Express ?
I followed a few Typescript examples but nothing for ES6.
I've looked at the generated ES5 from Typescript but this seems a backwards step
The documentation covers this (also can be seen here):
var inversify = require("inversify");
require("reflect-metadata");
var TYPES = {
Ninja: "Ninja",
Katana: "Katana",
Shuriken: "Shuriken"
};
class Katana {
hit() {
return "cut!";
}
}
class Shuriken {
throw() {
return "hit!";
}
}
class Ninja {
constructor(katana, shuriken) {
this._katana = katana;
this._shuriken = shuriken;
}
fight() { return this._katana.hit(); };
sneak() { return this._shuriken.throw(); };
}
// Declare as injectable and its dependencies
inversify.decorate(inversify.injectable(), Katana);
inversify.decorate(inversify.injectable(), Shuriken);
inversify.decorate(inversify.injectable(), Ninja);
inversify.decorate(inversify.inject(TYPES.Katana), Ninja, 0);
inversify.decorate(inversify.inject(TYPES.Shuriken), Ninja, 1);
// Declare bindings
var container = new inversify.Container();
container.bind(TYPES.Ninja).to(Ninja);
container.bind(TYPES.Katana).to(Katana);
container.bind(TYPES.Shuriken).to(Shuriken);
// Resolve dependencies
var ninja = container.get(TYPES.Ninja);
return ninja;
I want to have a variable in a TypeScript class that is of the type "boolean isVisible()".
How do I declare it?
How do I assign this function for another instantiated object to this variable?
How do I call this function?
ps - This seems so basic but 10 minutes of searching and I couldn't find it.
function boolfn() { return true; }
function strfn() { return 'hello world'; }
var x: () => boolean;
x = strfn; // Not OK
x = boolfn; // OK
var y = x(); // y: boolean
Here's one way of doing it, though I'll be happy to work with you to figure out exactly what you're trying to achieve.
export module Sayings {
export class Greeter {
isVisible(): boolean {
return true;
}
}
}
var greeter = new Sayings.Greeter();
var visible = greeter.isVisible();
You could also use a property instead of a function. Your original question talks about a "variable" and a "function" as if they're the same thing, but that's not necessarily the case.
export module Sayings {
export class Greeter {
isVisible: boolean = false;
}
}
var greeter = new Sayings.Greeter();
var visible = greeter.isVisible;
greeter.isVisible = true;
Or something like this maybe?
export module Sayings {
export class Greeter {
constructor(public isVisible: () => boolean) {
}
}
}
var someFunc = () => {
return false;
}
var greeter = new Sayings.Greeter(someFunc);
var visible = greeter.isVisible();
For instance, I have a library and I would like to protect the source code to being viewed. The first method that comes to mind is to create public wrappers for private functions like the following
function executeMyCoolFunction(param1, param2, param3) {
return executeMyCoolFunction_(param1, param2, param3);
}
Only public part of the code will be visible in this way. It is fine, but all Google Service functions look like function abs() {/* */}. I am curious, is there an approach to hide library source code like Google does?
Edit 00: Do not "hide" a library code by using another library, i.e. the LibA with known project key uses the LibB with unknown project key. The public functions code of LibB is possible to get and even execute them. The code is
function exploreLib_(lib, libName) {
if (libName == null) {
for (var name in this) {
if (this[name] == lib) {
libName = name;
}
}
}
var res = [];
for (var entity in lib) {
var obj = lib[entity];
var code;
if (obj["toSource"] != null) {
code = obj.toSource();
}
else if (obj["toString"] != null) {
code = obj.toString();
}
else {
var nextLibCode = exploreLib_(obj, libName + "." + entity);
res = res.concat(nextLibCode);
}
if (code != null) {
res.push({ libraryName: libName, functionCode: code });
}
}
return res;
}
function explorerLibPublicFunctionsCode() {
var lstPublicFunctions = exploreLib_(LibA);
var password = LibA.LibB.getPassword();
}
I don't know what google does, but you could do something like this (not tested! just an idea):
function declarations:
var myApp = {
foo: function { /**/ },
bar: function { /**/ }
};
and then, in another place, an anonymous function writes foo() and bar():
(function(a) {
a['\u0066\u006F\u006F'] = function(){
// here code for foo
};
a['\u0062\u0061\u0072'] = function(){
// here code for bar
};
})(myApp);
You can pack or minify to obfuscate even more.
Edit: changed my answer to reflect the fact that an exception's stacktrace will contain the library project key.
In this example, MyLibraryB is a library included by MyLibraryA. Both are shared publicly to view (access controls) but only MyLibraryA's project key is made known. It appears it would be very difficult for an attacker to see the code in MyLibraryB:
//this function is in your MyLibraryA, and you share its project key
function executeMyCoolFunction(param1, param2, param3) {
for (var i = 0; i < 1000000; i++) {
debugger; //forces a breakpoint that the IDE cannot? step over
}
//... your code goes here
//don't share MyLibraryB project key
MyLibraryB.doSomething(args...);
}
but as per the #megabyte1024's comments, if you were to cause an exception in MyLibraryB.doSomething(), the stacktrace would contain the project key to MyLibraryB.