Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular CLI - How to share prototype functions across the application

I need to have some global prototype functions on the string class. Eg.

string.prototype.trimWhiteSpaces = function () {
  return this.replace(/ +/g, '');
}

I am using Angular CLI and I want this function to be accessible to all strings across my Angular 4 app. I have added the code snippet to a file called prototypes.js and in the .angular-cli.json I loaded the file

  "scripts": [
      "assets/js/prototypes.js",
      "../node_modules/jquery/dist/jquery.min.js",
      "../node_modules/bootstrap/dist/js/bootstrap.min.js",
      "../node_modules/moment/min/moment.min.js"
    ],

However, I keep getting the following error when I compile my project

Property 'trimWhiteSpaces' does not exist on type 'string'.

How do I make such functions accessible throughout my application

like image 716
lohiarahul Avatar asked Nov 02 '17 00:11

lohiarahul


3 Answers

The problem is TypeScript doesnt know about these type definitions.


Quick Method

Provide definitions for each of the methods you're using

Open typings.d.ts and add the following:

interface String {
  trimWhiteSpaces: () => string;
}

You will have to provide definitions for each function you're consuming. While this is faster, it may be a good time to reevaluate prototypes.js and make it TypeScript friendly.


Pro Method

Convert the Library to typescript and import/export functions as needed. This is more time consuming, but if you own the library it's something you're going to want to do eventually.

If you wanted to update the library and still use the prototype (which doesnt treeshake well) you would do something like this:

File: string-prototypes.ts

String.prototype.trimWhiteSpaces = trimWhiteSpaces;

interface String {
  trimWhiteSpaces: typeof trimWhiteSpaces;
}

function trimWhiteSpaces() {
  return this.split(' ').join('');
}

At the top of your app.module.ts import this file like so:

import './string-prototypes';

The second approach would be to structure your library like this, and import the functions as needed.

File: string-helpers.ts

export function trimWhiteSpaces(input: string) {
  return input.split(' ').join('');
}

In a component:

import { trimWhiteSpaces } from './string-helpers';

You loose the prototype augmentation this way, but it guarantees consumers of your library are only using what they need.

like image 141
cgatian Avatar answered Nov 10 '22 03:11

cgatian


I had the urge to prototype some methods too

We're working on a project that uses thousands of classes and members, a large amount of these members are completely repetitive and could be shared among the components

after a few minutes searching I came across these solution Angular Global or Shared Modules

Generally there's 2 ways to handle it

  1. You need to share a particular code among specific components, and not globally

this would happen to any medium to large web application

Angular Docs explained how to do so

  1. You need to share some code globally (which this is your case i guess)

Note that This option is different than just any ordinary global module , we want to populate some prototype with our own functionality

This Article Easily Shows how to populate prototypes for our own use inside an Angular App

like image 2
Ali Sawari Avatar answered Nov 10 '22 02:11

Ali Sawari


You can simply declare such interface as global in a separate file and export it so that typescript knows about it.

So your file e.g. prototypes.ts should look like this:

export {}; // Using the export, there is no need to import the file anywhere.

declare global {
    interface String {
        trimWhiteSpaces(): string;
    }

    interface Array<T> {
        isEmpty(): boolean;
        isNotEmpty(): boolean;
    }
}

if (!String.prototype.trimWhiteSpaces)
    String.prototype.trimWhiteSpaces = function (): string {
        return this.replace(/ +/g, '');
    }
}

if (!Array.prototype.isEmpty) {
    Array.prototype.isEmpty = function <T>(): boolean {
        return this?.length === 0;
    };
}

if (!Array.prototype.isNotEmpty) {
    Array.prototype.isNotEmpty = function <T>(): boolean {
        return this?.length > 0;
    };
}
like image 2
hovado Avatar answered Nov 10 '22 04:11

hovado