Ionic 2 Angular 2 Global Import Extension methods - javascript

I've made some extensions to the Date prototype like:
interface Date {
YearsFromToday(): number;
}
Date.prototype.YearsFromToday = function (): number {
// implementation
}
I'm using the ionic2 tutorial --v2 template, which is a pretty standard layout - app.html, app.ts, app.module etc.
I was wondering if there was an easy way to have this declared globally. I'm not sure exactly where to put this in the project?

Put your monkey patch code in a file.
You might call it monkey-patch-date.ts, for example:
monkey-patch-date.ts
interface Date {
YearsFromToday(): number;
}
Date.prototype.yearsFromToday = function (): number {
// implementation
}
and then import it in main.ts or whatever your entry module is:
main.ts
import './monkey-patch-date';
Alternately. You can make it a module that exports its monkey-patcher if you want to be extra explicit to call out that you are doing something dangerous.
monkey-patch-date.ts
declare global {
interface Date {
yearsFromToday(): number;
}
}
export default function () {
Date.prototype.yearsFromToday = function (): number {
// implementation
};
}
And import it like
main.ts
import monkeyPatchDate from './monkey-patch-date';
monkeyPatchDate();
Another alternative, especially useful for library authors is to allow monkey-patching but not require it while still exposing the functionality.
Here is an example:
date-augmentations/index.ts
export function yearsFromToday(date: Date): number {
// implementation
}
date-augmentations/monkey-patch.ts
import {yearsFromToday} from './index';
declare global {
interface Date {
yearsFromToday(): number;
}
}
Date.prototype.yearsFromToday = function() {
return yearsFromToday(this);
}
Now a consumer can either monkey patch the Date prototype by running
import 'date-augmentations/monkey-patch';
Can access the functionality by the export without monkey patching anything
import {yearsFromToday} from 'date-augmentations';
const date = new Date('12-12-2023');
const yft = yearsFromToday(date);
console.log(yft); // prints 6

Related

How to create a definition for 'module.exports' function

I am exporting a function like this which works.
module.exports = function (options: any): RequestHandler {
// Do stuff
}
I am trying to add a definition for the exported function, but I am not sure if this is the right way to do it:
declare global {
export function tsm(options: any): RequestHandler
}
When I try to test it both of the following say that this is valid:
const tsm = require('ts-middleware')
global.tsm() // Gives intellisense
tsm() // Also gives intellisense
It shouldn't give information about global.tsm(), so I think that I created my definition wrong. What is the correct way to create a function definition?
I don't want to use the function like this:
const tsm = require('ts-middleware')
tsm.tsm()
But I do want to use it like this:
const tsm = require('ts-middleware')
tsm()
To define typings for a module, use declare module 'x' {...}. i.e.:
declare module 'ts-middleware' {
export = (option: any): RequestHandler
}
However, you do not really need to do that because you wrote your code in TypeScript. The compiler can generate the typings for you automatically.
You just need to add declaration: true in your tsconfig.json:
// tsconfig.json
{
"compilerOptions": {
"declaration": true
}
}
Also, I would strongly recommend you to write your code using ESM. i.e. instead of module.exports = ...:
// named export, preferred
export function foo(options: any): RequestHandler { ... }
// or default export
export default function foo(options: any): RequestHandler { ... }
// import in TypeScript for named export
import { foo } from 'ts-middleware'
// import in TypeScript for default export
import foo from 'ts-middleware'
// require in JavaScript commonjs
const middleware = require('ts-middleware')
middleware.foo(...)

Mocking named imports and constructors ES6 & Ava

I have a class constructor, with a function I want to stub:
class Service {
constructor(){}
async someFunction() {
try {
// does stuff
}
catch (e) {}
}
}
In the file I want to test, this is imported an used like so:
const { Service } = require('something')
const newService = new Service('xyz')
I'm struggling to get this to import & stub correctly in my tests.
Currently am importing like this:
t.context.service = {
Service: class Service {
constructor () {
this.someFunction = sinon.stub()
}
}
}
This import seems to work, but then I can't get a reference back to it through the constructed version. Any help on this one?
I want to be able to make an assertion like:
t.true(t.context.service.Service.someFunction.calledOnce)
AVA doesn't provide any stubbing. Have a look at https://github.com/testdouble/testdouble.js/ or http://sinonjs.org/.

what's the proper way to implement this custom typescript module?

Let's say I want to implement and use the following ts module. It's just a basic validator that validates a first name:
export namespace Validators
{
export class NameValidator
{
constructor()
{
}
FirstNameIsValid(firstName: string)
{
return firstName.length < 20;
}
}
}
What would be the correct way for me to implement the module above? Also, what would be the correct way for me to reference and use this module from my ng2 component? The following import statement doesn't work:
import { Validators.NameValidator } from './modules/name-validator';
The way I have done it in the past is to create a module
module Formatter{
export class SsnFormatter {
ssn: string;
constructor(unformattedSsn: string) {
this.ssn = unformattedSsn.replace(/(\d{3})(\d{2})(\d{4})/, '$1-$2-$3');
}
formatSsn() {
return this.ssn;
}
}
}
Then within the other typescript files call it as
let f = new Formatter.SsnFormatter('111111111');
console.log(f);
As long as your class is within the same module namespace you should be able to reference it directly.
This was from an angular 1.x project but the version of TS or angular should not matter this is typescript communicating between modules.

Correctly writing and importing custom node modules

EDIT
This is NOT a TypeScript question but rather a best-practices one. The example in TypeScript but really, the question here is how do I, correctly, expose multiple exports from multiple files that compose a module, and how to correctly import them.
EDIT 2
There's still something not right
So, on my Module2's index I have this:
export * from "./queryFilter";
On Module1 user.ts I import it by using
import { queryFilter as QueryFilter } from "Module2";
(...)
var User = {...}
User.getById = (userId: string) => {
...
}
export { User }
And my Module1's index.ts I export it as
export * from "./model/user";
Then, on my main project I import it using
import * as Schema from "Module1";
var User = Schema.User;
However, this throws an error whenever I try to call User.getById:
Debug: internal, implementation, error
TypeError: Uncaught error: Cannot read property 'getByEmail' of undefined
From this approach, what am I doing wrong???
I'm writing a couple of node modules for the first time ever and I do have some questions regarding proper declaration/usage of this.
So I'm creating a module that will require another custom module, let's call them Module1 and Module2.
So, Module1 is required by the main application, but Module1 also requires Module2.
Now on Module2 I have a bunch of files and on each one I export what I need. Here's a sample:
Module2 - Utils.ts:
"use strict"
const RandomString = require("randomstring");
let randomString = (numCharacters: number) => {
return RandomString.generate({
length: numCharacters,
capitalization: 'uppercase',
charset: 'alphanumeric'
});
}
module.exports.randomString = randomString;
Module2 - queryFilter.ts:
"use strict"
export default class QueryFilter {
name: string;
op: string;
value: any;
scope: string;
constructor(name: string, op: string, value: any, scope: string) {
this.name = name;
this.op = op;
this.value = value;
this.scope = scope;
}
public static GetConditionQuery(filters: QueryFilter[], params: string[]) {
(...)
}
public static queryComparator(value1: any, value2: any, operator: string): any {
(...)
}
}
On Module1, I added Module2 to package.json so when I want to use QueryFilter, for instance, I require it like this:
Module1 - Class.ts:
import { QueryFilter } = require("Module2").queryFilter;
Now my question is, is it enough to export individual items from each file in my Module2 and use it in this fashion? Or should I have a index that would export every file from Module2 so that Module1 can see them?
Something along the lines of:
Module2 - index.ts:
export "./utils"
export "./queryFilter"
(...)
What is the correct way of doing this? I'm a total newb to this and the docs I've read didn't shed any light on this..
Best Regards
Following your last comment, this the 'typescript solution' for creating a module/library. The solution would be similar for "pure" ES6; typings in Typescript just add a small layer of difficulty.
Assuming you write a module/library with multiple files:
func1.ts
export func1(param1: number): string { ... }
func2.ts
export func2(): number { ....}
Create an index.ts which re-exports the interesting parts of your module/lib
export * from './func1'
export * from './func2'
....
Obviously you can choose to (re)export and make public only part of the code e.g. export { thisFunc, thatClass } from './anotheFile'
Compile the module with declarations and commonjs flags, and make sure that the package.json main points to index.js, while the typings entry points to the generated index.d.ts
This module/library can now be used from Javascript or Typescript using an ES6 style import syntax
import { func1, func2 } from 'mymodule'
If this is Typescript, the typings of the 2 functions will automatically be imported too.

How do I split a TypeScript class into multiple files?

I found a lot of examples and also tried myself to split a module into several files. So I get that one, very handy. But it's also practical sometimes to split a class for the same reason. Say I have a couple of methods and I don't want to cram everything into one long file.
I'm looking for something similar to the partial declaration in C#.
Lately I use this pattern:
// file class.ts
import { getValue, setValue } from "./methods";
class BigClass {
public getValue = getValue;
public setValue = setValue;
protected value = "a-value";
}
// file methods.ts
import { BigClass } from "./class";
function getValue(this: BigClass) {
return this.value;
}
function setValue(this: BigClass, value: string ) {
this.value = value;
}
This way we can put methods in a seperate file. Now there is some circular dependency thing going on here. The file class.ts imports from methods.ts and methods.ts imports from class.ts. This may seem scary, but this is not a problem. As long as the code execution is not circular everything is fine and in this case the methods.ts file is not executing any code from the class.ts file. NP!
You could also use it with a generic class like this:
class BigClass<T> {
public getValue = getValue;
public setValue = setValue;
protected value?: T;
}
function getValue<T>(this: BigClass<T>) {
return this.value;
}
function setValue<T>(this: BigClass<T>, value: T) {
this.value = value;
}
You can't.
There was a feature request to implement partial classes, first on CodePlex and later on GitHub, but on 2017-04-04 it was declared out-of-scope. A number of reasons are given, the main takeaway seems to be that they want to avoid deviating from ES6 as much as possible:
TypeScript already has too many TS-specific class features [...] Adding yet another TS-specific class feature is another straw on the camel's back that we should avoid if we can. [...] So if there's some scenario that really knocks it out of the park for adding partial classes, then that scenario ought to be able to justify itself through the TC39 process.
I use this (works in typescript 2.2.2):
class BigClass implements BigClassPartOne, BigClassPartTwo {
// only public members are accessible in the
// class parts!
constructor(public secret: string) { }
// this is ugly-ish, but it works!
methodOne = BigClassPartOne.prototype.methodOne;
methodTwo = BigClassPartTwo.prototype.methodTwo;
}
class BigClassPartOne {
methodOne(this: BigClass) {
return this.methodTwo();
}
}
class BigClassPartTwo {
methodTwo(this: BigClass) {
return this.secret;
}
}
I use plain subclassing when converting large old multi file javascript classes which use 'prototype' into multiple typescript files:
bigclassbase.ts:
class BigClassBase {
methodOne() {
return 1;
}
}
export { BigClassBase }
bigclass.ts:
import { BigClassBase } from './bigclassbase'
class BigClass extends BigClassBase {
methodTwo() {
return 2;
}
}
You can import BigClass in any other typescript file.
A modified version of proposed pattern.
// temp.ts contents
import {getValue, setValue} from "./temp2";
export class BigClass {
// #ts-ignore - to ignore TS2564: Property 'getValue' has no initializer and is not definitely assigned in the constructor.
public getValue:typeof getValue;
// #ts-ignore - to ignore TS2564: Property 'setValue' has no initializer and is not definitely assigned in the constructor.
public setValue:typeof setValue;
protected value = "a-value";
}
BigClass.prototype.getValue = getValue;
BigClass.prototype.setValue = setValue;
//======================================================================
// temp2.ts contents
import { BigClass } from "./temp";
export function getValue(this: BigClass) {
return this.value;
}
export function setValue(this: BigClass, value: string ) {
this.value = value;
}
Pros
Doesn't create additional fields in class instances so there is no overhead: in construction, destruction, no additional memory used. Field declations in typescript are only used for typings here, they don't create fields in Javascript runtime.
Intellisence is OK (tested in Webstorm)
Cons
ts-ignore is needed
Uglier syntax than #Elmer's answer
The rest properties of solutions are same.
Modules let you extend a typescript class from another file:
user.ts
export class User {
name: string;
}
import './user-talk';
user-talk.ts
import { User } from './user';
class UserTalk {
talk (this:User) {
console.log(`${this.name} says relax`);
}
}
User.prototype.sayHi = UserTalk.prototype.sayHi;
declare module './user' {
interface User extends UserTalk { }
}
Usage:
import { User } from './user';
const u = new User();
u.name = 'Frankie';
u.talk();
> Frankie says relax
If you have a lot of methods, you might try this:
// user.ts
export class User {
static extend (cls:any) {
for (const key of Object.getOwnPropertyNames(cls.prototype)) {
if (key !== 'constructor') {
this.prototype[key] = cls.prototype[key];
}
}
}
...
}
// user-talk.ts
...
User.extend(UserTalk);
Or add the subclass to the prototype chain:
...
static extend (cls:any) {
let prototype:any = this;
while (true) {
const next = prototype.prototype.__proto__;
if (next === Object.prototype) break;
prototype = next;
}
prototype.prototype.__proto__ = cls.prototype;
}
You can use multi file namespaces.
Validation.ts:
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
}
LettersOnlyValidator.ts (uses the StringValidator from above):
/// <reference path="Validation.ts" />
namespace Validation {
const lettersRegexp = /^[A-Za-z]+$/;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
}
Test.ts (uses both StringValidator and LettersOnlyValidator from above):
/// <reference path="Validation.ts" />
/// <reference path="LettersOnlyValidator.ts" />
// Some samples to try
let strings = ["Hello", "101"];
// Validators to use
let validators: { [s: string]: Validation.StringValidator; } = {};
validators["Letters only"] = new Validation.LettersOnlyValidator();
Why not just use Function.call that js already comes with.
class-a.ts
Class ClassA {
bitten: false;
constructor() {
console.log("Bitten: ", this.bitten);
}
biteMe = () => biteMe.call(this);
}
and in other file bite-me.ts
export function biteMe(this: ClassA) {
// do some stuff
// here this refers to ClassA.prototype
this.bitten = true;
console.log("Bitten: ", this.bitten);
}
// using it
const state = new ClassA();
// Bitten: false
state.biteMe();
// Bitten: true
For more information have a look at the definition of Function.call
Personally I use #partial decorator acts as a simplified syntax that may help divide functionality of a single class into multiple 🍬🍬🍬 class files ... https://github.com/mustafah/partials
// Declaration file
class BigClass {
declare method: (n: number, s: string) => string;
}
// Implementation file
BigClass.prototype.method = function (this: BigClass, n: number, s: string) {
return '';
}
The downside of this approach is that it is possible to declare a method but to forget to actually add its implementation.
We can extend class methods gradually with prototype and Interface definition:
import login from './login';
import staffMe from './staff-me';
interface StaffAPI {
login(this: StaffAPI, args: LoginArgs): Promise<boolean>;
staffsMe(this: StaffAPI): Promise<StaffsMeResponse>;
}
class StaffAPI {
// class body
}
StaffAPI.prototype.login = login;
StaffAPI.prototype.staffsMe = staffsMe;
export default StaffAPI;
This is how i have been doing it
Mixins approach
To add to #Elmer's solution, I added following to get it to work in separate file.
some-function-service-helper.ts
import { SomeFunctionService } from "./some-function-service";
export function calculateValue1(this: SomeFunctionService) {
...
}
some-function-service.ts
import * as helper from './some-function-service-helper';
#Injectable({
providedIn: 'root'
})
export class SomeFunctionService {
calculateValue1 = helper.calculateValue1; // helper function delcaration used for getNewItem
public getNewItem() {
var one = this.calculateValue1();
}

Categories

Resources