typescript 基于代码动态缩小字符串文本类型

0qx6xfy6  于 2023-01-21  发布在  TypeScript
关注(0)|答案(1)|浏览(137)

抱歉,如果标题不准确,这是一个奇怪的问题.
下面的代码:

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> = {

}

我的目标是使用接口FileLoggerNetworkLogger或任何其他实现来创建LegalLoggerKeys。正如您所看到的我的注解代码,我能够使用硬编码的字符串常量来完成它。
是否可以使用接口创建相同的内容?Playground Link

mpgws1up

mpgws1up1#

编译器不能自动发现所有声明为扩展Logger的接口,所以你需要自己手动维护这样一个列表,其中一个方法是使用联合:

type KnownLoggers = NetworkLogger | FileLogger // <-- maintain this manually

一旦你有了这样一个并集,你就可以操作它来生成可接受的键的并集。如果你有一个扩展Logger的日志类型L,你可以通过template literal type

type LoggerKey<L extends Logger> = `${L['type']}.${L['provider']}`;

type NetworkLoggerKey = LoggerKey<NetworkLogger>;
// type NetworkLoggerKey = "http.datadog"

但是如果你有一个记录器类型的联合体,这就不是你想要的方式了,因为连接字符串的每一段都将被分别拆分成联合体:

type KnownLoggerKeys = LoggerKey<KnownLoggers>
// type KnownLoggerKeys = "file.datadog" | "file.log4j" | "http.datadog" | "http.log4j" 👎

哎呀。
因此我们需要调整LoggerKey的定义,使其在输入中的联合体之间 * 分布 * 其操作,我们可以将其表示为一个分布条件类型:

type LoggerKey<L extends Logger> =
  L extends Logger ? `${L['type']}.${L['provider']}` : never;

注意,检查L extends Logger ?作为一个检查实际上并不有用;编译器已经知道L被约束到Logger,但是它用作在求值之前将L拆分成其并集成员然后在求值之后重新组合成新的并集的指令。
所以现在我们有:

type KnownLoggerKeys = LoggerKey<KnownLoggers>
// type KnownLoggerKeys = "http.datadog" | "file.log4j" 👍

如所期望的。
Playground代码链接

相关问题