Why I don't get compile time error when I am not implementing some method from my interface which is implemented in base class? - javascript

I have the following code
interface IDownload {
downloadFile(): void;
}
class BaseClass implements IDownload {
downloadFile(): void {
console.log('some logic here');
}
}
class Sub extends BaseClass {
}
so my sub class has access to the methods from BaseClass because we are extending from that class.
My base class implement some method from interface.
When i try to extend the class and implement again the same interface
class Sub extends BaseClass implements IDownload {
}
I don't get compile time error that i need to implement the method from IDownload. I guees it is like that because it sees that the base class already implements it.
But i want to have that check also here in my sub class because i want to have the interface contract in the base class where all the methhods will exist on the sub class.
How can i do this ?

If you want all the implementation in the sub class, why don't you just implement the interface in the sub class?
interface IDownload {
downloadFile(): void;
}
class BaseClass {
}
class Sub extends BaseClass implements IDownload {
downloadFile(): void {
console.log('some logic here');
}
}

Related

Typescript optional parameters overload

I have a class e.g.
class SomeClass {
someMethod(param1: string; param2?: string) { ... }
}
And another one that extends from fore
class AnotherClass extends SomeClass {
someMethod(param1: string) { ... }
}
Also I've got a third class that uses instance of one of these classes as generic e.g.
class ThirdClass<T extends SomeClass> {
instanceClass: T;
}
So I'm getting an error that someMethod in AnotherClass is not compatible with SomeClass, to solve the problem I should make an overload for someMethod in SomeClass :
someMethod(param1: string); // <-- overload
someMethod(param1: string; param2?: string) { ... }
Is there any other way to say TS that everything is ok?

How to make a field required in all children in TypeScript?

Suppose I have a class Base, how do I make all children inherited from Base implements a certain field on its own?
For example, Base has a required member function func(), A extends Base and B extends A. I want to make func() definition required in both A and B.
Can I do this and how? Thanks!
To make definition required you have to use interfaces:
interface MyPerfectlyNamedInterface {
func(): any; // no definition here, since interfaces are inherently abstract
}
class Base implements MyPerfectlyNamedInterface {
func(): any { /* definition is required */ }
}
// note that A does _not_ extend Base
class A implements MyPerfectlyNamedInterface {
func(): any { /* definition is required */ }
}
class B implements MyPerfectlyNamedInterface {
func(): any { /* definition is required */ }
}
However, if child class extends base class that has definition for your function (e.g., if A would extend Base and/or B would extend A), then definition is again not required:
interface MyPerfectlyNamedInterface {
func(): any;
}
class Base implements MyPerfectlyNamedInterface {
func(): any { /* definition is required */ }
}
// note that A _does_ extend Base
class A extend Base implements MyPerfectlyNamedInterface {
func(): any { /* definition is not required */ }
}
class B extend A implements MyPerfectlyNamedInterface {
func(): any { /* definition is not required */ }
}
So, I guess, that's not really possible with your setup.
Also, you can sort of achieve this using abstract classes, but again, it won't "cascade" down to child classes as you want:
abstract class Base {
abstract func(): any;
}
class A extends Base {
func(): any { /* definition is required */ }
}
class B extends A {
func(): any { /* definition is not required */ }
}
You can mark the method abstract in base class like this.
abstract class Base {
abstract func(): void;
}
class A extends Base {
func(): void {
console.log("Doing something in A class");
}
}
class B extends Base {
func(): void {
console.log("Doing something in B class");
}
}

Typescript abstract optional method

I have an abstract class with some abstract methods. Is there a way to mark some of these methods as optional?
abstract class Test {
protected abstract optionalMethod?(): JSX.Element[];
protected abstract requiredMethod(): string;
}
For some reasons I can add the ? as a suffix but it seems like it does nothing at all because I have to still implement the method in the derived class. For now I am using it like this to mark it that it can return null which is basically the poor mans optional.
protected abstract optionalMethod?(): JSX.Element[] | null;
You can do this with class and interface merging:
interface Foo {
print?(l: string): void;
}
abstract class Foo {
abstract baz(): void;
foo() {
this.print && this.print('foo');
}
}
class Bar extends Foo {
baz(): void {
if (this.print) {
this.print('bar');
}
}
}
Link to the above code in the Typescript playground
I'm not sure if this changed at some point, but today (TS 4.3) you can simply make the base-class optional method non abstract:
abstract class Base {
protected abstract required(): void;
protected optional?(): string;
print(): void {
console.log(this.optional?.() ?? "Base");
}
}
class Child1 extends Base {
protected required(): void { }
}
class Child2 extends Base {
protected required(): void { }
protected optional(): string {
return "Child";
}
}
const c1 = new Child1();
c1.print();
const c2 = new Child2();
c2.print();
Try it on the TS Playground.
The concept of abstract is something which is not defined but will be in the inherited classes. That is why we can't have abstract methods without implementation.
I suggest you to create non-abstract method already implemented in your base class to achieve your goal:
abstract class A {
protected abstract requiredMethod(): string;
protected emptyDefinition(): string | void {};
protected optionalMethod(): string {
return "something optional";
};
}
class B extends A {
protected requiredMethod(): string {
return "something required";
}
}
Typescript doesn't support the "omission" of optional abstract functions, but you can explicitly leave it undefined as below:
abstract class Test {
protected abstract optionalMethod?(): JSX.Element[];
protected abstract requiredMethod(): string;
}
class MyTest extends Test {
protected optionalMethod: undefined;
protected requiredMethod(): string {
return 'requiredResult';
}
}
You don't need interface merging.
You only have to work with overload methods without default implementations like so:
abstract class Test {
...
private someMethod() {
...
this.optinalMethod?.();
}
...
protected abstract requiredMethod(): string;
protected optionalMethod?(): string;
}
abstract indicated that it has to be overwritten in the implementing class.
in case of an optional method we however don't want to enforce overriding it.
This has been around for a while but and answered correctly above, but the example isn't clear. You can use interface merging like this too.
interface Test {
optionalMethod?(): JSX.Element[];
}
abstract class Test {
protected abstract requiredMethod(): string;
}

Assigning child class to a parent class typed property

I do have the following 2 base clases:
class BaseModel {}
class BaseService{
protected model:BaseModel;
}
Now I want to implement BaseHelper and BaseService for a specific use case and assign a derived class to my property.
class MyModel extends BaseModel{
constructor(param:string){
super();
}
}
class MyService extends BaseService {
model = MyModel;
}
However, this gives me the error Type 'typeof MyModel' is not assignable to type 'BaseModel'.
Important: I want to attach the class MyModel, not an instance of the class MyModel!
You need to instantiate MyModel using the new keyword (new MyModel()).
You assigned the actual class (model = MyModel) instead of an instance of it.
Also, you might want to make BaseService generic:
class BaseModel {}
class BaseService<T extends BaseModel> {
protected model: T;
constructor(model: T) {
this.model = model;
}
}
class MyModel extends BaseModel{}
class MyService extends BaseService<MyModel> {
constructor() {
super(new MyModel());
}
}
(code in playground)
Edit
If you need the class and not the instance, then something like:
class BaseModel {}
type BaseModelConstructor = { new(): BaseModel };
class BaseService {
protected modelCtor: BaseModelConstructor;
}
class MyModel extends BaseModel {}
class MyService extends BaseService {
modelCtor = MyModel;
}
(code in playground)
Or you can use generics here as well:
class BaseModel {}
type BaseModelConstructor<T extends BaseModel> = { new(): T };
class BaseService<T extends BaseModel> {
protected modelCtor: T;
}
class MyModel extends BaseModel {}
class MyService extends BaseService<BaseModel> {
modelCtor = MyModel;
}
(code in playground)
If your derived classes have different ctor signatures then you can either deal with it in the base ctor type:
type BaseModelConstructor<T extends BaseModel> = { new(...args: any[]): T };
Here you can pass any count and kind of parameters, but you can also supply different signatures:
type BaseModelConstructor<T extends BaseModel> = {
new(): T;
new(str: string): T;
new(num: number, bool: boolean): T;
};
But you can also use a different type per derived class:
type MyModelConstructor = { new(param: string): MyModel };
model should be an instance of MyModel:
class MyModel extends BaseModel{}
class MyService extends BaseService {
model = new MyModel()
}
Use instance of BaseModel
class MyService extends BaseService {
model = new MyModel();
}

Multiple Class Inheritance In TypeScript

What are ways to get around the problem of only being allowed to extend at most one other class.
class Bar {
doBarThings() {
//...
}
}
class Bazz {
doBazzThings() {
//...
}
}
class Foo extends Bar, Bazz {
doBarThings() {
super.doBarThings();
//...
}
}
This is currently not possible, TypeScript will give an error. One can overcome this problem in other languages by using interfaces but solving the problem with those is not possible in TypeScript.
Suggestions are welcome!
This is my workaround on extending multiple classes. It allows for some pretty sweet type-safety. I have yet to find any major downsides to this approach, works just as I would want multiple inheritance to do.
First declare interfaces that you want to implement on your target class:
interface IBar {
doBarThings(): void;
}
interface IBazz {
doBazzThings(): void;
}
class Foo implements IBar, IBazz {}
Now we have to add the implementation to the Foo class. We can use class mixins that also implements these interfaces:
class Base {}
type Constructor<I = Base> = new (...args: any[]) => I;
function Bar<T extends Constructor>(constructor: T = Base as any) {
return class extends constructor implements IBar {
public doBarThings() {
console.log("Do bar!");
}
};
}
function Bazz<T extends Constructor>(constructor: T = Base as any) {
return class extends constructor implements IBazz {
public doBazzThings() {
console.log("Do bazz!");
}
};
}
Extend the Foo class with the class mixins:
class Foo extends Bar(Bazz()) implements IBar, IBazz {
public doBarThings() {
super.doBarThings();
console.log("Override mixin");
}
}
const foo = new Foo();
foo.doBazzThings(); // Do bazz!
foo.doBarThings(); // Do bar! // Override mixin
This is possible with interfaces:
interface IBar {
doBarThings();
}
interface IBazz {
doBazzThings();
}
class Foo implements IBar, IBazz {
doBarThings() {}
doBazzThings(){}
}
But if you want implementation for this in a super/base way, then you'll have to do something different, like this:
class FooBase implements IBar, IBazz{
doBarThings() {}
doBazzThings(){}
}
class Foo extends FooBase {
doFooThings(){
super.doBarThings();
super.doBazzThings();
}
}
Not really a solution to your problem, but it is worth to consider to use composition over inheritance anyway.
Prefer composition over inheritance?

Categories

Resources