I currently have a software which it’s comportment change depending on which operating system it’s installed (such as Windows
or Linux
).
So I just want to create an object fitting the operating system depending of a static config variable config.soft.type
(a mere string equal to "windows"
or "linux"
).
At program’ startup, my class need to synchronize so I put the constructor private and added an async static synchronize()
method for both classes which will create and return the OperatingSystem
object for Windows or Linux.
How to properly instantiate a repository for both Windows
and Linux
classes with their proper types ?
// index.ts
...
const { type }: 'windows' | 'linux' = config.soft;
const software = type === 'windows'
? new Windows.synchronize(toto, tata, tutu)
? new Linux.synchronize(toto, tata, tutu);
software.down();
aRandomFunction(software);
...
I would like to pass software
(of type Windows | Linux
) to other function (such as aRandomFunction()
) and use OperatingSystem
as type for software
(because both Windows
and Linux
classes come from OperatingSystem
, but I don’t know if I need to explicitely put “basic” functions (shared but overloaded by both classes) in OperatingSystemInterface
)
My classes / interfaces are defined as follow:
// OperatingSystem's Interface,
abstract class OperatingSystemInterface
// The implementation of OperatingSystemInterface, I expect Windows and Linux to have `down()` method defined in OperatingSystem without redeclaring it in their interfaces
class OperatingSystem implements OperatingSystemInterface
// OperatingSystem for Windows, with overloaded methods to fitt Windows's need and some more method / properties specific to Windows's class
class Windows extends OperatingSystem implements WindowsInterface
// OperatingSystem for Linux
class Linux extends OperatingSystem implements LinuxInterface
currently, I’m getting errors, such as:
error TS2420: Class 'Windows' incorrectly implements interface 'WindowsInterface'.
Property 'synchronize' is missing in type 'Windows' but required in type 'WindowsInterface'.
8 export class Windows extends OperatingSystem implements WindowsInterface {
~~~~~
src/services/OperatingSystems/types.ts:70:3
70 synchronize(
~~~~~~~~~~~~
71 client: Client,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...
74 toto: string
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
75 ): Promise<WindowsInterface>;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'synchronize' is declared here.
But I did declare it, is it because the prototype isn’t matching ? (In the Interface I declared synchronize()
‘s return type of the *Interface and in the class I return an object of the implemented class itself (Linux instead of LinuxInterface, for example), but should I import Linux
in types.ts
to put it as return type in LinuxInterface
‘s synchronize()
? it doesn’t feel right.
Complete code:
// types.ts (where I stored my interfaces)
import { Client } from 'client';
import { Config } from 'config';
export abstract class OperatingSystemInterface {
abstract readonly client: Client;
abstract readonly config: Config;
abstract down(): void;
}
export interface WindowsInterface extends OperatingSystemInterface {
start(): Promise<void>;
someWindowsRelatedMethod(): Promise<number>;
anotherWindowsMethod(path: string, value: number): Promise<void>;
synchronize(
protected client: Client,
protected config: Config,
toto: string
): Promise<WindowsInterface>;
}
export interface LinuxInterface extends OperatingSystemInterface {
start(): Promise<void>;
synchronize(
protected client: Client,
protected config: Config,
toto: string
): Promise<LinuxInterface>;
}
// OperatingSystem.ts
// Base class which `Windows` and `Linux` overload methods
export class OperatingSystem implements OperatingSystemInterface {
public readonly informations: Information[] = [];
constructor(
protected client: Client,
protected config: Config,
toto: string,
) {
this._toto = toto;
this.client.subscribe(`${config.message}/toto/tata`);
this.client.subscribe(`${config.message}/titi`);
}
get toto(): string {
return this._toto;
}
down(): void {
this.client.stop();
}
}
// Linux.ts
import { Client } from 'client';
import { Config } from 'config';
export class Linux extends OperatingSystem implements LinuxInterface {
private constructor(
protected client: Client,
protected config: Config,
toto: string
) {
super(client, config, toto);
}
static async synchronize(
client: Client,
config: Config,
toto: string
): Promise<Linux> {
const operatingSystem = new Linux(client, config, toto);
// here some specific code to Linux
...
return operatingSystem;
}
async start(): Promise<void> {
this.client.on('message', (message: Message) => {
// some Linux's specific code
});
}
}
// Windows.ts
import { Client } from 'client';
import { Config } from 'config';
export class Windows extends OperatingSystem implements WindowsInterface {
// it represent the Application's permission over the Windows's API
private windowsInfo: Config = {
role: 'Viewer',
isSuperAdmin: true,
};
private constructor(
protected client: Client,
protected config: Config,
toto: string
) {
super(client, config, toto);
}
static async synchronize(
client: Client,
config: Config,
toto: string
): Promise<Windows> {
const operatingSystem = new Windows(client, config, toto);
// here some Windows' specific code
...
return operatingSystem;
}
async start(): Promise<void> {
this.client.on('message', (message: Message) => {
// some Window's specific code
this.someWindowsRelatedMethod();
});
}
someWindowsRelatedMethod(): Promise<number> {
...
}
anotherWindowsMethod(path: string, value: number): Promise<void> {
...
}
}