Reputation: 119
I'm trying to combine 2 separate Typescript methods into one with the same name getDevice
. First function requires just a number to return a Device
or null
in case there's no device:
protected getDevice(deviceId: number): Device | null {
const device = this.devicesService.getDevice(deviceId);
if (device == null)
console.warn(`Failed to get the device #${deviceId}.`);
return device;
}
The second function takes 2 arguments. First one is either a number (like previous function) or a Device
(result of previous function):
protected getDeviceAs<T extends DeviceType>(
deviceOrId: Device | number,
deviceType: (new (device: Device) => T),
): T | null {
const device = typeof deviceOrId === 'number'
? this.devicesService.getDevice(deviceOrId)
: deviceOrId as Device;
if (device == null) {
console.warn(`Failed to get the device #${deviceOrId}.`);
return null;
}
return new deviceType(device);
}
A result of combining two methods would be something like this:
protected getDevice<T extends DeviceType>(
deviceOrId: Device | number,
deviceType: (new (device: Device) => T) | null = null,
): Device | T | null {
let device: Device | null = null;
// In case deviceOrId is a number
if (typeof deviceOrId === 'number') {
device = this.devicesService.getDevice(deviceOrId);
if (device == null) {
console.warn(`Failed to get the device #${deviceOrId}.`);
return null;
}
if (deviceType == null) return device;
}
// getDeviceAs functionality
return new deviceType(device);
}
The problem is I can't wrap my head how to properly type the whole function:
deviceOrId
argument type:
deviceOrId
is a Device
, then for sure the result must be T | null
deviceOrId
is a number
, then its could be Device | T | null
deviceOrId
depend on the deviceType
argument:
deviceType
is a null
then the deviceOrId
must be of type number
and return type of Device | null
deviceType
is the (new (device: Device) => T)
then the return type must be T | null
Is it even possible in Typescript? If yes how? Maybe with some function overloading tricks?
Upvotes: 0
Views: 64
Reputation: 119
There's an easy solution to the problem. You can overload functions in typescript (documentation). First you declare you function signatures. In case of this question it would be:
protected getDevice(deviceId: number): Device | null;
protected getDevice<T extends DeviceType>(
deviceOrId: Device | number,
deviceType: (new (device: Device) => T),
): T | null;
Then you write the function implementation that will handle the logic. That implementation function signature won't be considered as another overload. Full code:
// Overload 1:
protected getDevice(deviceId: number): Device | null;
// Overload 2:
protected getDevice<T extends DeviceType>(
deviceOrId: Device | number,
deviceType: (new (device: Device) => T),
): T | null;
// Implementation:
protected getDevice<T extends DeviceType>(
deviceOrId: Device | number,
deviceType: (new (device: Device) => T) | null = null,
): Device | T | null {
let device: Device | null = null;
if (typeof deviceOrId === 'number') {
device = this.devicesService.getDevice(deviceOrId);
if (device == null) {
console.warn(`Failed to get the device #${deviceOrId}.`);
return null;
}
if (deviceType == null) return device;
}
return new deviceType(device);
}
Upvotes: 1