Inheriting static attributes in Typescript without using the static keyword

My project involves utilizing multiple classes that represent entities from a database.

abstract class Entity {
    static columns: Column[];
    static showInNav: boolean;
    static dependencies: string[];
    // non-static fields
}
class Entity_A extends Entity {
    //static properties redeclaration
    //non-static properties
}
class Entity_B extends Entity {
    //static properties redeclaration
    //non-static properties
}

Each class extends the Entity class or one of its children. During the initialization phase, I store these classes in an array [Entity_A, Entity_B, ...], iterate over them, and retrieve their properties to properly configure the application. These static properties serve as the configuration settings.

The challenge arises because TypeScript lacks a static contract, making it prone to mistakes and difficult to debug (which is not considered best practice). One potential solution could be to convert static properties to methods and access them using new currentClass().property. However, I believe there must be a more efficient approach.

Any suggestions?

Edit (desired outcome): I aim to securely define "configuration" within classes (including typechecking and mandatory overrides) while easily accessing this information when provided with an array of classes.

Answer №1

To maintain the integrity of your code and ensure that required static fields are specified, consider hiding the actual Entity class within a module and only exposing a function that constructs a derived class with overridden static fields. By encapsulating this logic in a function, you can enforce the implementation of essential properties for each entity:

entity.ts

abstract class EntityImpl {
    static columns: Column[];
    static showInNav: boolean;
    static dependencies: string[];
    abstract doStuff(): void;
}
export interface Column {
    // Placeholder for demonstration purposes
}

export function Entity(required: { columns: Column[]; showInNav: boolean; dependencies: string[];}) {
    abstract class Entity extends EntityImpl {
        static columns: Column[] = required.columns
        static showInNav: boolean = required.showInNav;
        static dependencies: string[] = required.dependencies;
    }
    return Entity;
}
// Export type definitions for flexible usage 
export type Entity = EntityImpl;
export type EntityClass = typeof EntityImpl;

impl.ts

import { Entity, EntityClass } from './entity'

class Entity_A extends Entity({
    columns: [],
    dependencies: [],
    showInNav: true
}) {
    doStuff(): void {} // Abstract methods must be implemented 
}

class Entity_B extends Entity({
    columns: [],
    dependencies: [],
    showInNav: false
}) {
    doStuff(): void {}
}

// Accessing static properties for all classes
var allClasses : Array<EntityClass> = [Entity_A, Entity_B];
for(let type of allClasses) {
    console.log(type.showInNav);
}

// Accessing instances with ensured method implementation
var data: Array<Entity> = [new Entity_A(), new Entity_B()];
data.forEach(x => x.doStuff());

This approach enforces the specification of static fields and ensures the implementation of abstract methods. For base classes deriving from Entity, the same pattern can be applied by encapsulating the class creation logic within a function.

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

TypeScript's type casting will fail if one mandatory interface property is missing while an additional property is present

While running tsc locally on an example file named example.ts, I encountered some unexpected behavior. In particular, when I created the object onePropMissing and omitted the property c which is not optional according to the interface definition, I did not ...

Using parameters in routes in Angular 4

all I have created a new auxiliary website. Users will be directed to this site from the main site using a reference link like: I have set up the AppRoutingModule as follows: import { NgModule } from '@angular/core'; import { RouterMod ...

All-encompassing NextJS App router with a focus on Internationalization

I am currently working with a folder structure that includes an optional catch-all feature. The issue I am facing is that the page does not change when the URL is altered to include ""/"" or ""/about-us"". It consistently remains on the ...

Tips for effectively passing navigation as props in React Navigation with Expo

How can I correctly pass navigation as props to another component according to the documentation? The navigation prop is automatically provided to each screen component in your app. Additionally, To type check our screens, we need to annotate the naviga ...

A step-by-step guide on customizing the background color of a Dialog in Angular Material (Version 16)

I've been attempting to modify the background color of my Angular Material Dialog by utilizing the panelClass property in the MatDialogConfig. Unfortunately, I'm encountering a partial success. I am aiming to set the background color as red (jus ...

Does this Spread Operator Usage Check Out?

Upon reviewing Angular's API documentation, I came across the declaration for the clone() method in HttpRequest as follows: clone(update: { headers?: HttpHeaders; reportProgress?: boolean; params?: HttpParams; responseType?: "arraybuffer" ...

Discover the process of retrieving all workday dates using Angular

Currently, I am working on a project in Angular that involves allowing employees to record their work hours. However, I am facing a challenge in figuring out how to gather all the work dates and store them in an array. Here is what I have attempted so fa ...

"Encountering a 400 bad request error when making a Graphql POST

Seeking assistance with my graphql code. I have included the service and component files below. I am currently new to graphql and not utilizing the apollo client; instead, I am attaching a query on top of the HTTP POST call to send requests to the graphql ...

How is it possible that there is no type error when utilizing copy with spread syntax?

When I use the map function to make a copy of an array of objects, why doesn't it throw an error when adding a new property "xxx"? This new property "xxx" is not declared in the interface. interface A{ a:number; b:string; }; let originalArray:A[] ...

Mastering Props Typing in React Using TypeScript

Currently, I am faced with the task of defining the following: interface MyCompProps { someAttr: number } Instead of having to explicitly list all the aria-* attributes I need upfront, I would like to simply use aria- and other normal HTML attributes ...

I am trying to replace the buttons with a dropdown menu for changing graphs, but unfortunately my function does not seem to work with the <select> element. It works perfectly fine with buttons though

I am currently working on my html and ts code, aiming to implement a dropdown feature for switching between different graphs via the chartType function. The issue I am facing is that an error keeps popping up stating that chartType is not recognized as a ...

Upgrading from Angular 5.2 to 6.0: Facing an issue where angular.json does not replace angular-cli.json

After diligently following the official documentation to upgrade from Angular 5.2 to Angular 6.0 (with the end goal of migrating to Angular 13), I found myself at a standstill. Executing the command NG_DISABLE_VERSION_CHECK=1 npx @angular/cli@6 update @an ...

Mastering the Conversion from NGRX Effect to NGRX Effect v15

I am currently working on converting the code snippet to NGRX 15. As a newcomer to Angular, I could use some guidance. "@ngrx/effects": "^15.4.0" @Injectable() export class SnackbarEffects { @Effect({ dispatch: false }) cl ...

incorporating a personalized HTMLElement using Typescript

Hey there! I'm fairly new to using Angular and could use some help. I'm trying to insert a custom HTML element onto a page when a button is clicked, but I'm struggling to figure it out. Here are my HTML and TypeScript files: TypeScript Fil ...

Declaring a function type with a void parameter type in typescript

Embarking on my journey with ts and currently exploring TypeGraphQL. I came across something that caught my attention and seems unfamiliar to me: export declare type ReturnTypeFunc = (returns?: void) => ReturnTypeFuncValue; How should this type be unde ...

Type definition for Vuex store functionality

Working on creating a versatile type to provide typing hints for mutations in Vuex. After reading an inspiring article on Vuex + TypeScript, I decided to develop something more generic. Here is what I came up with: export type MutationType<S, P, K exten ...

What is the best way to insert CSS code into a custom Vue directive file?

I need a solution that applies a gradient background to the parent div in case an image fails to load. I've attempted to create a directive for this purpose: export default { bind(el: any, binding: any) { try { ..... ...

What is the best way to simulate an overloaded method in jest?

When working with the jsonwebtoken library to verify tokens in my module, I encountered a situation where the verify method is exported multiple times with different signatures. export function verify(token: string, secretOrPublicKey: Secret, options?: Ve ...

Develop an asynchronous thunk with TypeScript in Redux Toolkit, utilizing the features of rejectWithValue and Payload types for handling errors

Struggling to integrate an authentication slice into Redux Toolkit using TypeScript, facing errors related to rejectWithValue and action payload types. Utilizing Axios and following the documentation, but TypeScript is still flagging issues in my code. im ...

What is the best way to implement an Angular application within the child routes of another Angular application?

Is it possible to load an Angular app using lazy-loading (when a specific route is hit by users) into another Angular app without compiling the first one for use in the second? This is the Routing-Module of the app to nest into an Angular app: const upgr ...