Creating an extended class in Typescript with a unique method that overrides another method with different argument types

I need to change the argument types of a method by overriding it:

class Base {
    public myMethod(myString: string): undefined {
        return;
    }
}

class Child extends Base {
    public myMethod(myNumber: number): undefined {
        return super.myMethod(String(myNumber));
    }
}

However, I encounter a TypeScript error:

Property 'myMethod' in type 'Child' is not assignable to the same property in base type 'Base'. Type '(myNumber: number) => undefined' is not assignable to type '(myString: string) => undefined'. Types of parameters 'myNumber' and 'myString' are incompatible. Type 'string' is not assignable to type 'number'.

Is there a way to achieve this without triggering a TypeScript error?

Answer №1

It is not possible to achieve this*. In TypeScript, subtyping comes with inheritance, making it impossible to inherit from a base class without being a valid subtype of it.

* There are some technical workarounds available, but they are considered messy hacks that should be avoided at all costs.

Answer №2

It has been mentioned by various individuals that straying from Liskov substitution is not advisable.

An alternative approach would be to create an override method that accepts both strings and numbers. This ensures compatibility with the base class wherever it is used.


class Base {
     public myMethod(myString: string): undefined {
         return;
     }
 }

class Child extends Base {
    public myMethod(myNumberOrString: number | string): undefined {
        if (typeof myNumberOrString === 'number') {
            return super.myMethod(String(myNumberOrString));
        } else {
            return super.myMethod(myNumberOrString);
        }
    }
}

Answer №3

There appears to be a workaround that functions, even for return types, specifically with TypeScript version 4.02 by utilizing an interim class.

However, it should be noted that this method violates the Liskov substitution principle as it alters the return type and does not account for scenarios where the parameter is a string.

While it's interesting from a knowledge standpoint, I wouldn't endorse this approach in a code review since a subclass should seamlessly replace the base class without impacting functionality.

class Base {
    public myMethod(myString: string): string {
        return myString + myString;
    }
}

// weaken
// inspired by comment here: https://github.com/microsoft/TypeScript/issues/3402#issuecomment-385975990
class Interim extends Base {
    public myMethod(x: any): any { return super.myMethod(x); }
}

class Child extends Interim {
    public myMethod(myNumber: number): number {
        return myNumber * myNumber;
    }
}

// we can have a helper/utility for this
function weaken(klass: { new(...args: any[]): any; }, key: string) {
    return class extends klass {
        [key](...args: any[]) { super[key](...args); }
    }
}


class ChildOther extends weaken(Base, 'myMethod') {
    public myMethod(myNumber: number): number {
        return myNumber * myNumber;
    }
}

console.log((new Child()) instanceof Base); // true
console.log((new ChildOther()) instanceof Base); // true
console.log(new Base().myMethod('str')); // strstr
console.log(new Child().myMethod(3)); // 9
console.log(new ChildOther().myMethod(3)); // 9

Answer №4

If you want to achieve this easily, consider creating a common BaseClass and extending both of your classes from it. Ensure that any functions with different parameters are only defined in the extended classes to avoid overlapping. Here's a simple demonstration (with a unique function named uniqueFunction):

    class BaseClass { // Both FirstClass and SecondClass extend this base class
        constructor() {
            this.title = ""
        }
        title: string
    }

    class FirstClass extends BaseClass {
        uniqueFunction(title: string) { // This function has one parameter
            this.title = title
        }
    }

    class SecondClass extends BaseClass {
        constructor() {
            super()
            this.count = 0
        }
        uniqueFunction(title: string, count: number) { // This function has two parameters
            this.title = title
            this.count = count
        }
        count: number
    }

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

Exploring Angular 7: Understanding the HTML5 Fullscreen API and Overcoming Errors

I am currently using Angular 7 and I am trying to implement a fullscreen button in my app. I have utilized the HTML5 Fullscreen API and created two functions for this purpose: openfullscreen() { // Trigger fullscreen console.log('gg'); ...

What is the process for importing a map from an external JSON file?

I have a JSON file with the following configuration data: { "config1": { //this is like a map "a": [ "string1", "string2"], "b": [ "string1", "string2"] } } Previously, before transitioning to TypeScript, the code below worked: import ...

An issue with TypeORM syntax causing errors within a NestJS migration file

I recently encountered an issue while setting up PostgreSQL with NestJS and TypeORM on Heroku. Despite successfully running a migration, my application kept crashing. I attempted various troubleshooting methods by scouring through blogs, GitHub issues, and ...

Exploring the depths of TypeScript's intricate type validation

Could you please review the comments in the code? I am trying to determine if it is feasible to achieve what I need or if TypeScript does not support such functionality. I require type checks for my addToRegistry function. Play around with TypeScript in ...

Encountering 'null' error in template with Angular 4.1.0 and strictNullChecks mode

After updating Angular to version 4.1.0 and activating "strictNullChecks" in my project, I am encountering numerous errors in the templates (.html) that look like this: An object may be 'null' All these errors are pointing to .html templat ...

Offering a limited selection of generic type options in TypeScript

Is there a shorthand in TypeScript for specifying only some optional types for generic types? For example, let's say I have a class with optional types: class GenericClass<A extends Type1 = Type1, B extends Type2 = Type2, C extends Type3 = Type3> ...

Sometimes encounter undefined values after assigning them through the service

One of the challenges I am facing is handling public fields in my service: @Injectable() export class ShareDataService { // Some code public templateForCurrencyCOS; public templateForPercentCOS; public templateForCurrencyCOGS; public te ...

Bidirectional enumeration in TypeScript

I am working with an enum defined as: enum MyEnum { key1 = 'val1' key2 = 'val2' } However, I am unsure how to create a SomeType implementation that fulfills the following requirements: Function: const myFunction = (param: SomeT ...

connecting and linking template content with an Observable

I have a CRUD page that needs to be updated after every operation. I have implemented Observable and the CRUD functions (specifically Add and Delete) are working fine, but I need to manually refresh the page to see the changes reflected. After trying to ...

Can builtins like DOM globals be explicitly imported?

The present situation includes the utilization of rollup (as well as iife parameters), but I am hesitant about whether it is solely related to rollup or typescript. My objective is to achieve something similar to this: import { document } from "[wherever ...

Building TypeScript Model Classes

Greetings! As a newcomer to TypeScript with a background in both C# and JavaScript, I am on a quest to create class models resembling those found in C#. Here's my attempt so far: export class DonutChartModel { dimension: number; innerRadius: ...

What is the best way to loop through the keys of a generic object in TypeScript?

When I come across a large object of unknown size, my usual approach is to iterate over it. In the past, I've used generators and custom Symbol.iterator functions to make these objects iterable with a for..of loop. However, in the ever-evolving world ...

Having problems with template refs staying null when using TypeScript in Nuxt3?

My Images.vue parent component contains 3 child components: ImagesGridArea1, ImagesGridArea2, and ImagesGridArea3. The children are tagged like this: <ImagesGridArea1 ref="ref1" :key="ref1key" ... />. In the parent script, I have: ...

Limit function parameters to only accept values with matching keys

I am relatively new to using TypeScript and am currently working on a project that involves handling various shapes of data from different sources. My goal is to pass this data to different aggregator classes, with one aggregator class corresponding to eac ...

Can the detectChanges() method in Angular cause any issues when used with the form control's valueChanges event?

Within my parent Component, I am working with a formGroup and updating its value using patchValue method. ngAfterViewInit() { this.sampleform.controls['a'].patchValue ...} I then pass this form to a child component in the parent component's ...

The configuration of the property has not been declared (Error: <spyOnProperty>)

Imagine having a MenuComponent @Component({ selector: 'cg-menu', templateUrl: './menu.component.html', styleUrls: [ './menu.component.scss' ] }) export class MenuComponent implements OnInit { menu: MenuItem[]; isLog ...

Random Angular template string that does not contain a templateRef

Could a string retrieved from an external resource, such as an http response, be passed as a dynamic template that binds to the component's instance without relying on TemplateRef? Context: Consider having an AppComponent with at least one variable n ...

Dynamic Type in Typescript Record

Looking for a way to attach types to record names in a class that returns a Record. The current code snippet is as follows: interface DataInterface { bar: number; foo: string; fooBar: boolean; } export class MyClass { public bar: number; p ...

class-validator: ensures the correct number of digits are present in numeric values

Seeking assistance on validating the number of digits for numeric values using class-validator. Specifically, I want my entity to only accept numbers with 6 digits for a certain property. For example: const user1 = new User(); user1.code = 123456 // should ...

What are some strategies for distinguishing between callable and newable types?

I seem to be facing a challenge related to narrowing, specifically the differentiation between Fnc (callable) and Class (newable). The code snippet provided functions in Playground, but the autocomplete feature is lacking, as shown in the first image. My ...