我正在开发一个软件,需要在满足某些条件时触发警报。
send(message:string)
作为发送警报的方法。abstract class Alert {
abstract send(message:string):Promise<void> {}
}
class DiscordAlert extends Alert {
constructor(private discordHookUrl:string){}
async send(message:string) {
console.log(`Sending Discord alert to ${this.discordHookUrl}`);
}
}
class TelegramAlert extends Alert {
constructor(private token:string){}
async send(message:string) {
console.log(`Sending Telegram alert to ${this.token}`);
}
}
我注意到 DiscordAlert 和 TelegramAlert 都有不同的构造函数参数。 因为discord 使用
discordHookUrl
而 telegram 使用 token
。
现在的问题是我想为警报创建一个工厂,但不知道如何传递配置。
class AlertFactory {
static createAlert(type, config?????) {
switch (type) {
case 'discord':
return new DiscordAlert(config???);
case 'telegram':
return new TelegramAlert(config???);
default:
throw new Error(`Alert type ${type} not supported.`);
}
}
}
我怎样才能做到这一点?
Record<string,string>
这样的配置并在创建任何警报之前进行运行时类型检查吗?通常最好的解决方法是让你的函数采用元组的并集。
例如:
function foo(...args: [type: 'a', value: string] | [type: 'b', value: number]) {
//...
}
foo('a', 'a string') // fine
foo('b', 123) // fine
foo('a', 456) // error
这表示该函数接受两个参数。它们可以是
'a'
和 string
,也可以是 'b'
和数字。但它们不能是'a'
和一个数字。
但是我们需要派生一些不同的类型才能使您的代码正常工作。
为了更好地测试事物,让我们确保两个更改构造函数采用不同的类型。
class DiscordAlert extends Alert {
constructor(private discordHookUrl: string) { super() }
async send(message: string) {}
}
class TelegramAlert extends Alert {
constructor(private token: { jwt: string }) { super() }
async send(message: string) {}
}
现在我们需要一个类型来将
discord
这样的字符串映射到该名称的构造函数。可能看起来像这样:
type AlertMapping = {
discord: typeof DiscordAlert,
telegram: typeof TelegramAlert,
}
请注意,我们希望
typeof MyClassHere
获取类的 constructor 的类型,而不是该类的实例的类型。
从中我们可以将元组与映射类型进行并集:
type AlertArgs = {
[K in keyof AlertMapping]: [
type: K,
config: ConstructorParameters<AlertMapping[K]>[0]
]
}[keyof AlertMapping]
这映射了
AlertMapping
中的所有键,并为每个键创建一个元组类型,其中第一个元素是键本身K
,第二个是在AlertMapping
中找到的构造函数的第一个参数的类型在那个键上。
现在您可以将该类型用于您的函数函数参数:
class AlertFactory {
static createAlert(...[type, config]: AlertArgs): unknown {
switch (type) {
case 'discord':
return new DiscordAlert(config);
case 'telegram':
return new TelegramAlert(config);
default:
throw new Error(`Alert type ${type} not supported.`);
}
}
}
现在我们可以调用此方法,第一个参数
type
将根据构造函数的需要与第二个参数config
强链接。
// Fine
const discordAlert = AlertFactory.createAlert('discord', 'hookUrlHere')
const telegramAlert = AlertFactory.createAlert('telegram', { jwt: 'abc123' })
// Error as expected
const discordAlertBad = AlertFactory.createAlert('discord', { jwt: 'abc123' })