2

I have a class MyClass that uses another class MainProvider. The thing is that the normal MainProviderclass doesn't work like I want, so I created another class MyProvider which has the same functions (implement the same interface as Provider).

My problem is that I can't use MyProvider in my class (MyClass), because even if the class MyProvider implements the same interface, there is a mismatch with a property (here getFetchUrl).

It is a private property in the MainProvider class, so it's not present in the interface. Still, I can't use MyProvider in my class. I get this error: Argument of type 'MyProvider' is not assignable to parameter of type 'MainProvider'. Property 'getFetchUrl' is missing in type 'MyProvider' but required in type 'MainProvider'. I tried to add it in my own class, but get this error instead Argument of type 'MyProvider' is not assignable to parameter of type 'MainProvider'. Types have separate declarations of a private property 'getFetchUrl'.

How can I use one as the substitute for the other ?

Example:

declare abstract class ProviderInterface {
    abstract baseUrl: string;
    abstract foo(): void;
}

class MainProvider implements ProviderInterface {
    baseUrl: string;
    private getFetchUrl: string;

    constructor(baseUrl: string) {
        this.baseUrl = baseUrl;
    }

    foo() {
        return "hello";
    }

}

class MyProvider implements ProviderInterface {
    private _baseUrl: string;
    private getFetchUrl: string;

    constructor(baseUrl: string) {
        this._baseUrl = baseUrl;
    }

    foo() {
        return "hey";
    }

    get baseUrl() {
        return this._baseUrl;
    }
}

class MyClassUsingProvider {
    private _provider: MainProvider;
    constructor(provider: MainProvider) {
        this._provider = provider;
    }
}

const myProvider = new MyProvider("http://127.0.0.1:3000");
const myClass = new MyClassUsingProvider(myProvider);
Cizia
  • 428
  • 5
  • 11
  • That's a lot of code, and it's incomplete, so it's hard to validate the types for myself. If you create a [minimal self contained reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) we can probably help you more. Something like, perhaps, [this](https://tsplay.dev/mp91MW)? – Alex Wayne Jun 04 '22 at 06:21
  • Also private properties in an abstract class doesn't make much sense. `abstract` fields are public, and private properties are not available for use in subclasses. `implement`ing an interface is really just about implementing the _public_ interface, and has nothing to with runtime implementation details like private properties. Simplify your question with a minimal example and maybe we can help find a solution here. But as is, it's hard to advise. – Alex Wayne Jun 04 '22 at 06:30
  • Hey, sorry for being unclear, I hope this is more understandable. The interface doesn't implement the private properties, but it seems like they are required to use a class as a substitute for the other. I don't understand how I can use my own implementation instead of the main one. It seems that whatever I do, I'll get an error. – Cizia Jun 04 '22 at 07:00
  • Does this answer you question? https://stackoverflow.com/questions/48953587/typescript-class-implements-class-with-private-functions With the `PublicPart` type there you can ignore the private and therefore un-providable properties. Like this: https://tsplay.dev/m3yYLw – Alex Wayne Jun 04 '22 at 07:26
  • Yes it does, thank you so much for the directions – Cizia Jun 04 '22 at 08:07

1 Answers1

0

So the issue was coming from the fact that private members of a class are visible to other instances of the same class, though private members are required when trying to substitute a class to another.

So what you want to do is to change the type to a PublicParts type, where PublicParts mean only the publics properties are required, and T is the type you want the public properties from.

This fixes the example above, and also my code:

type PublicPart<T> = {[K in keyof T]: T[K]}

declare abstract class ProviderInterface {
    abstract baseUrl: string;
    abstract foo(): void;
}

class MainProvider implements ProviderInterface {
    baseUrl: string;
    private getFetchUrl: string;

    constructor(baseUrl: string) {
        this.baseUrl = baseUrl;
    }

    foo() {
        return "hello";
    }

}

class MyProvider implements ProviderInterface {
    private _baseUrl: string;
    private getFetchUrl: string;

    constructor(baseUrl: string) {
        this._baseUrl = baseUrl;
    }

    foo() {
        return "hey";
    }

    get baseUrl() {
        return this._baseUrl;
    }
}

class MyClassUsingProvider {
    private _provider: PublicPart<MainProvider>;
    constructor(provider: PublicPart<MainProvider>) {
        this._provider = provider;
    }
}

const myProvider = new MyProvider("http://127.0.0.1:3000");
const myClass = new MyClassUsingProvider(myProvider);
Cizia
  • 428
  • 5
  • 11