Using TypeScript to ensure class parameter types without affecting properties

I am tasked with defining a schema for "operations" that will be used in my application. This schema must be easily extendable for other groups of "operations" and should include a dictionary of settings for each keyword.

Eventually, a "generic caller" will need to receive the type of the "operation" along with the keyword, and then cache it for future calls.

It is crucial for this "generic caller" to verify that the requested keyword is defined within the operation at compile time to catch errors early on in the development process.

Below is a solution that closely aligns with what I am looking for:

// Operation interface
interface Operation {
    url: string
    parameters: Record<string,string>
}

// Operations schema
class Operations {}
class BaseOperations extends Operations {   
    one: {
        url: '/one',
        parameters: {p: '1'}
    }
    two: {
        url: '/two',
        parameters: {}
    }
}

// Generic caller (with caching functionality)
function runOperation<T extends Operations>(type: {new(): T;}, keyword: keyof T) {
    let operation = new type();
    //cache_and_run(operation[keyword]);
}

// Main
function main() {
    runOperation(BaseOperations, 'one');
    runOperation(BaseOperations, 'two');
    runOperation(BaseOperations, 'three'); // Expected VS Code error
}

The only issue here is that the parameters defined in Operations are not bound to the Operation interface. While this is a minor problem, I aim to ensure that both ends (operation definitions and their usage) are validated at compile time.

Further research led me to the use of "index signature" parameter, which enforces the returned type:

class BaseOperations extends Operations {
    [x:string]: Operation
    one: {
        url: '/one',
        parameters: {p: '1'}
    }
    two: { // 'uarl' is not assignable
        uarl: '/two'
    }
}

However, this method disabled the 'one'|'two' check in runOperation, as any string is now considered a valid keyof BaseOperations.

Do you have any suggestions or solutions?

Answer №1

To enhance type safety in your class declaration, consider using an `implements` clause instead of an index signature. By implementing a self-referential type like

Record<keyof BaseOperations, Operation>
, you can instruct the compiler to ensure that `BaseOperations` complies with the implemented type:

class BaseOperations extends Operations 
    implements Record<keyof BaseOperations, Operation> {

    one!: {
        url: '/one',
        parameters: { p: '1' }
    }
    two!: { // error! 'uarl' is not assignable
        uarl: '/two'
    }

}

An error occurs at the `two` declaration above, which can be rectified for successful compilation. Despite this, the compiler retains knowledge about the keys present on `BaseOperations`, preventing key set widening to `string`:

runOperation(BaseOperations, 'one'); // okay
runOperation(BaseOperations, 'two'); // okay
runOperation(BaseOperations, 'three'); // error!

Explore the code further in the Playground link.

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

Is it possible to retrieve a trimmed svg image and store it on a device using react-native-svg in React Native?

I have a modified image that I want to save in my device's gallery. Can someone please guide me on how to achieve this? My project is developed using TypeScript. Modified image: https://i.stack.imgur.com/LJOY9.jpg import React from "react"; ...

I'm curious, which ref tag should I utilize for draft.js?

I'm currently working on a form using Draft.js with Next.js and TS, but I've encountered some errors in my code. Here is what I have so far: import {Editor, EditorState} from 'draft-js'; const [editorState, setEditorState] = useState( ...

Changing function arguments in TypeScript using the spread operator

Could the Tuple spreading syntax in Typescript be utilized to consolidate these function overloads? The challenge lies in the necessity to refactor the function arguments into new types. type Type = TString | TNumber type TString = { tag: 'string&apos ...

Using the useContext hook across multiple files without needing to export it

I am working on a React app that has multiple states being managed function App(){ const [activeChoice, setActiveChoice] = useState("flights"); const [overlay, setOverlay] = useState(false); const [airports, setAirports] = useState([]); const [loading, ...

Unable to retrieve information from service using Angular 1.6 and TypeScript

I'm having an issue retrieving data from a Service in my Controller. Here is the code for my Service file: import {IHttpService} from 'Angular'; export class MyService { public static $inject = ['$http']; constructor(private $htt ...

Having difficulty choosing a default value from the Angular dropdown menu

My goal was to create a user-friendly address form that includes a country list for users to select when filling in their address information. The form is designed using ngForm, which not only collects the address but also allows users to edit their existi ...

Creating a form with required fields in Angular and using the ngIf directive

Update: modified the sample code to incorporate TypeScript for better clarity I have a form with various buttons for users to choose from. The submit button is initially disabled until a user selects a button. However, there's a unique requirement wh ...

TypeScript generic type and potential absence of a value

Recently, I've been delving into Facebook's immutable library and exploring their TypeScript bindings. Consider this snippet of code: const list: User[] = ...; list.map(user => ...) The lambda parameter user correctly has the type of User. ...

How can users create on-click buttons to activate zoom in and zoom out features in a Plotly chart?

I am currently working on an Angular application where I need to implement zoom in and zoom out functionality for a Plotly chart. While the default hoverable mode bar provides this feature, it is not suitable for our specific use case. We require user-cr ...

Incorporate a fresh attribute to the JSON data in an Angular API response

I'm currently working on updating my JSON response by adding a new object property. Below is an example of my initial JSON response: { "products": [{ "id": 1, "name": "xyz" }] } My goal is to include a new object property ca ...

Resolving conflicts between AbortSignal in node_modules/@types/node/globals.d.ts and node_modules/typescript/lib/lib.dom.d.ts within an Angular project

An issue occurred in the file node_modules/@types/node/globals.d.ts at line 72. The error message is TS2403: Subsequent variable declarations must have the same type. Variable 'AbortSignal' should be of type '{ new (): AbortSignal; prototype ...

Firebase Functions Project encountering a "Cannot find module" error in VS Code

While working on a firebase functions project in Visual Studio Code, I encountered an issue inside the index.ts file. The imported modules were not being recognized even though autocomplete showed that the modules exist. When attempting to import them, I k ...

Modify capital letters to dashed format in the ToJSON method in Nest JS

I am working with a method that looks like this: @Entity() export class Picklist extends BaseD2CEntity { @ApiHideProperty() @PrimaryGeneratedColumn() id: number; @Column({ name: 'picklist_name' }) @IsString() @ApiProperty({ type: Str ...

ReactJS does not support merging multiple pages into one based on user button selection

My goal is to dynamically load a component based on the user's current page. List of Pages: Executables Shop In the main screen, there is a sidebar with two icons. The primary button should set the Executables Page and the second button should set ...

There is no matching overload for this call in React Native

I am working on organizing the styles for elements in order to enhance readability. Here is the code I have written: let styles={ search:{ container:{ position:"absolute", top:0, }, } } After defining the s ...

showcasing products from database with the help of Angular 12

Here are the files related to the item: Item file And here is the component file: Component file Lastly, this is the data service file: Data Service file However, issues arise when testing the code with console log statements as it indicates that the ...

Creating a unique theme export from a custom UI library with Material-UI

Currently, I am in the process of developing a unique UI library at my workplace which utilizes Material-UI. This UI library features a custom theme where I have integrated custom company colors into the palette object. While these custom colors work perfe ...

Encountered error: Unable to locate module - Path 'fs' not found in '/home/bassam/throwaway/chakra-ts/node_modules/dotenv/lib' within newly generated Chakra application

Started by creating the app using yarn create react-app chakra-ts --template @chakra-ui/typescript. Next, added dotenv with yarn add dotenv Inserted the following code block into App.tsx as per the instructions from dotenv documentation: import * as dote ...

Using Vue.js 3 and Bootstrap 5 to Create a Custom Reusable Modal Component for Programmatically Showing Content

Trying to develop a reusable Modal Component using Bootstrap 5, Vuejs 3, and composible API. I have managed to achieve partial functionality, Provided (Basic Bootstrap 5 modal with classes added based on the 'show' prop, and slots in the body a ...

Parsing JSON objects with identifiers into TypeScript is a common task in web development

I possess a vast JSON object structured like so: { "item1": { "key1": "val1", "key2": "val2", "key3": [ "val4", "val5", ] }, { "item2": { "key1": "val1", "ke ...