抱歉,如果标题不准确,这是一个奇怪的问题.
下面的代码:
enum LogType {
file = "file",
http = "http",
database = "database"
}
interface LoggerDetails {
// ... bunch of properties
provider: "datadog" | "log4j" | "newrelic"
}
interface Logger {
type: LogType;
provider: LoggerDetails["provider"];
}
interface NetworkLogger extends Logger {
type: LogType.http,
provider: "datadog"
}
interface FileLogger extends Logger {
type: LogType.file,
provider: "log4j"
}
type LoggerKey = `${LogType}.${Logger["provider"]}`;
// type LegalLoggerKeys = Extract<LoggerKey, "http.datadog" | "file.log4j">;// <-- This works, but hard coded. Also, it requires us to write it down for every new logger interface
type LegalLoggerKeys = unknown; // ??
// should only accept 'http.datadog' and 'file.log4j' as keys using the interfaces that extends Logger
const record: Record<LegalLoggerKeys, (message: string) => void> = {
}
我的目标是使用接口FileLogger
、NetworkLogger
或任何其他实现来创建LegalLoggerKeys
。正如您所看到的我的注解代码,我能够使用硬编码的字符串常量来完成它。
是否可以使用接口创建相同的内容?Playground Link
1条答案
按热度按时间mpgws1up1#
编译器不能自动发现所有声明为扩展
Logger
的接口,所以你需要自己手动维护这样一个列表,其中一个方法是使用联合:一旦你有了这样一个并集,你就可以操作它来生成可接受的键的并集。如果你有一个扩展
Logger
的日志类型L
,你可以通过template literal type:但是如果你有一个记录器类型的联合体,这就不是你想要的方式了,因为连接字符串的每一段都将被分别拆分成联合体:
哎呀。
因此我们需要调整
LoggerKey
的定义,使其在输入中的联合体之间 * 分布 * 其操作,我们可以将其表示为一个分布条件类型:注意,检查
L extends Logger ?
作为一个检查实际上并不有用;编译器已经知道L
被约束到Logger
,但是它用作在求值之前将L
拆分成其并集成员然后在求值之后重新组合成新的并集的指令。所以现在我们有:
如所期望的。
Playground代码链接