Typescript patterns for creating a modular library design

Transitioning a JavaScript project to TypeScript has been challenging for me, especially when it comes to establishing a solid design pattern for the library's modularity.

Main Concept

The core functionality of my library is minimal. For instance, it may provide browser specifications as an object:

{
    browser:"firefox",
    version:"10.1.5",
    platform:"linux",
    screen-width:"1920",
    // .. etc
}

This library will be installed through NPM. However, to fully utilize its capabilities, users must install "modules" that process the above-mentioned object in unique ways.

For example, one module might identify which CSS features are unsupported in a particular browser version:

{
    unsupportedFeatures:[
        "x",
        "y",
        "z",
        // ... etc
    ]
}

Another module could take both the core library result and the previous module’s outcome to analyze the DOM for any unsupported CSS properties defined.

{
    notSupported:[
        "x",
        "y",
        // ... etc
    ]
}

Note: The mentioned functionalities are hypothetical examples illustrating how modules depend on the core library results as well as each other.


My Attempts So Far

I experimented with defining the core library as a function and adding modules as prototypes.

An issue I encountered was the lack of type definitions for both the core library and the added prototype functions.

I also explored defining the core library as a class, but this approach also led to compiler errors.

Lastly, merging namespace interface declarations yielded additional compilation issues.

Despite trying different solutions, none seemed to effectively work within the realm of TypeScript.

How can I tackle this challenge in TypeScript? What would be the ideal design pattern to follow for creating modular and extensible libraries while ensuring type safety and auto-completion?

Answer №1

Here is the modified playground code that reflects my personal touch:

// Central Library ---------------
namespace coreLib { 
    export interface customInterface { 
        browser: string,
        version: string
    }

    const modules: ((customInterface) => coreLib.customInterface)[] = [];
    export function attachModule(module: (customInterface) => coreLib.customInterface) { 
        modules.push(module);
    }

    export function CoreLibrary (): customInterface { 
        let result: customInterface = {
            browser: "",
            version: ""
        }
        // performing operations
        result.browser = "firefox";
        result.version = "1.0.0";
        modules.forEach((module) => { 
            result = module(result);
        })
        return result;
    }
}

// Extension Module ---------------
namespace coreLib {
    export interface customInterface {
        something?: boolean;
    }
    export function myExtension (input:coreLib.customInterface): coreLib.customInterface { 
        // additional functionality
        input.something = true;
        return input;
    } 
}

// End User Section ---------------
coreLib.attachModule(coreLib.myExtension);
const output = coreLib.CoreLibrary();
console.log(output.browser);
console.log(output.something);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to define the type of an object in TypeScript when passing it to a function

I am currently working on a function that accepts an object of keys with values that have specific types. The type for one field is determined by the type of another field in the same object. Here is the code: // Consider this Alpha type and echo function. ...

Exploring the new possibilities of Angular 5: Enhanced REST API service with paginated data and object mapping capabilities

After implementing pagination in my REST API backend, I now need to update my Angular services to accommodate the changes. Instead of simply returning an array of objects, the API will now return JSON responses structured like this: { "count": 0, ...

Utilizing a method from a separate class in Ionic 2

Having trouble using the takePicture() function from camera.ts in my home.ts. I keep getting an error message saying "No provider for CameraPage!" Any assistance on how to resolve this issue would be greatly appreciated, as I am new to this language and ju ...

When integrating the @azure/msal-angular import into the Angular application, the screen unexpectedly goes blank,

Starting a new Angular app and everything is rendering as expected at localhost:4200 until the following change is made: @NgModule({ declarations: [ AppComponent, HeaderBannerComponent, MainContentComponent, FooterContentinfoComponent ...

Mongoose encountered an error when attempting to cast the value "ObjectID" to an ObjectId at the specified path "red.s1"

My Mongoose schema is structured as follows: const gameSchema = new Schema({ matchNumber: { type: Number, required: [true, 'A match must have a number!'], unique: true }, red: { s1: { type: ...

Is it possible to utilize useEffect for verifying the existence of the user token within the localStorage?

I am in the process of developing a web application that requires authentication. I am wondering if it is effective to create a private route by adding a condition in the useEffect hook of one of my pages. The idea is to check if a token is present before ...

Encountering an issue with Jest when using jest.spyOn() and mockReturnValueOnce causing an error

jest --passWithNoTests --silent --noStackTrace --runInBand --watch -c jest-unit-config.js Project repository An error occurred at jest.spyOn(bcrypt, 'hash').mockRejectedValue(new Error('Async error message')) Error TS2345: The argum ...

Encountering difficulties accessing props while invoking a component in React

In my project, I've created a component called FilterSliders using Material UI. Within this component, I passed a prop named {classes.title} by destructuring the props with const { classes }: any = this.props;. However, when I try to access this prop ...

Determining if an emitted event value has been altered in Angular 4

I am currently working on an Angular 4 project. One of the features I have implemented is a search component, where users can input a string. Upon submission of the value, I send this value from the SearchComponent to the DisplayComponent. The process of ...

Discovering the array item by its ID using Angular 2 with Typescript

Hey everyone, I'm currently working with asp.net mvc 5 and running into an issue. When attempting to retrieve an object by its id, it keeps returning undefined. The strange thing is that the objects display fine when checking console.log(this.vtypes). ...

Mastering the Type Model on Firestore Function to Retrieve Field ValuesUnlock the potential of the Type

When retrieving data from Firestore, I am able to print the entire object using doc.data. However, I am having trouble accessing the specific value of unixtime as it is coming through as undefined. I need help in correctly applying my type model so that I ...

"Discover the power of Next.js by utilizing dynamic routes from a curated list

I am working on a Next.js application that has a specific pages structure. My goal is to add a prefix to all routes, with the condition that the prefix must be either 'A', 'B', or 'C'. If any other prefix is used, it should re ...

A more efficient method for querying documents based on ids that are not in a given list and then sorting them by a specific publish date

After implementing the code provided below, I noticed that the performance tests indicate each request takes a second or longer to complete. My goal is to enhance this speed by at least 10 times. The bottleneck seems to be caused by the NOT operator resu ...

Contrasting assign and modify operations on arrays

After spending 2 days searching for a bug in my angular2 project's service.ts file, I finally found it and fixed it. However, I'm still trying to understand why the working code behaves differently from the bugged one, as they appear identical to ...

Subclass method overloading in Typescript

Take a look at this example: class A { method(): void {} } class B extends A { method(a: number, b: string): void {} } An error occurs when trying to implement method() in class B. My goal is to achieve the following functionality: var b: B; b.met ...

Is there a way to automatically incorporate a component into every view within a Next.js application?

Is there a more efficient and less cumbersome way to import components for every view in a Next.js app? I am interested in incorporating the "arwes" framework into my project and utilizing components from . One of the examples of a component I will be usin ...

Encountered an error when incorporating nguniversal/express-engine into an Angular project: "Unable to locate the BrowserModule import in /src/app/app.module.ts"

One of the initial questions Purpose The main aim is to integrate SSR into my Angular project using ng add @nguniversal/express-engine --clientProject [name] (to enable dynamic prerendering of meta tags). Expected Outcome I anticipated the command to run ...

The TypeScript error message states, "The property 'breadcrumb' is not found within the type 'Data'."

My Breadcrumb Component is functioning properly when compiled to JavaScript and displaying the desired output. However, my IDE is showing an error message that I am struggling to fix. The error states: [ts] Property 'breadcrumb' does not exist o ...

Issue: Module '@nrwl/workspace/src/utilities/perf-logging' not found

I attempted to run an Angular project using nxMonorepo and made sure to install all the necessary node modules. However, when I tried running the command "nx migrate --run-migrations" in my directory PS C:\Users\Dell\Desktop\MEANAPP&bso ...

Why does the property of {{hero.name}} function properly in a <h> tag but not in an <img> tag?

Within this template, the code below functions correctly: <h3>{{hero.name}}</h3> It also works for: <a routerLink="/details/{{hero.id}}">{{hero.name}}</a> However, there seems to be an issue with the following image path ...