TypeScript实例化新类对象时联合类型错误的解决求助
问题描述
我在TypeScript中定义了一个Logger类,构造函数接受可选的options参数,类泛型C代表自定义日志级别,用于类中的log(level: Level<C>)等方法。我希望实现updateConfig方法,返回一个新的Logger实例,其C参数为原实例的自定义级别与新options对象中定义的级别联合类型。
当前代码存在问题:调用updateConfig后生成的newLogger无法识别新添加的自定义级别'test',且类型显示为Logger<"debug" | "trace"> | Logger<"debug" | "trace" | "test">,不符合预期。
当前代码:
type DefaultLevel = 'info' | 'error'; // Default log levels type Level<C extends string> = DefaultLevel | C type LevelWithSilence<C extends string> = Level<C> | 'silence' interface LoggerOptions<C extends string> { customLevels?: Record<C, number> level: LevelWithSilence<NoInfer<C>> } interface UpdatedLoggerOptions<C extends string, L extends string> { customLevels?: Record<L, number> level: LevelWithSilence<NoInfer<C | L >> } class Logger<C extends string = ''> { options?: LoggerOptions<C> constructor(options?: LoggerOptions<C>) { this.options = options } log(level: Level<C>, message: string) { // Log message } updateConfig<L extends string = never>(options?: UpdatedLoggerOptions<C, L>) { if(!this.options) return new Logger (options as LoggerOptions<C>) if(!options) return this return new Logger<C | L>({ customLevels: { ...this.options.customLevels as Record<C, number>, ...options.customLevels as Record<L, number> }, level: options.level } as LoggerOptions<C | L>) } } // Usage const logger = new Logger({ customLevels: { debug: 1, trace: 2, }, level: 'error' }); logger.log('debug', '') const newLogger = logger.updateConfig({ customLevels: { test: 3, }, level: 'test' }) newLogger.log('debug', '') // Works newLogger.log('test', '') // Should work
解决方案
问题核心在于updateConfig的返回值分支类型合并,以及泛型L的自动推导失效。以下是修正后的代码:
type DefaultLevel = 'info' | 'error'; // Default log levels type Level<C extends string> = DefaultLevel | C; type LevelWithSilence<C extends string> = Level<C> | 'silence'; // 简化配置接口,让泛型自动从customLevels键推导 interface LoggerOptions<C extends string = never> { customLevels?: Record<C, number>; level: LevelWithSilence<C>; } class Logger<C extends string = never> { options?: LoggerOptions<C>; constructor(options?: LoggerOptions<C>) { this.options = options; } log(level: Level<C>, message: string) { // Log message } // 明确返回类型,让L从传入的customLevels自动推导 updateConfig<L extends string = never>( options?: LoggerOptions<L> ): Logger<C | L> { // 合并原配置与新配置 const mergedOptions = this.options ? { customLevels: { ...this.options.customLevels, ...options?.customLevels, } as Record<C | L, number>, level: options?.level ?? this.options.level, } : options; return new Logger<C | L>(mergedOptions as LoggerOptions<C | L>); } } // Usage const logger = new Logger({ customLevels: { debug: 1, trace: 2, }, level: 'error', }); logger.log('debug', ''); // 正常运行 const newLogger = logger.updateConfig({ customLevels: { test: 3, }, level: 'test', }); newLogger.log('debug', ''); // Works newLogger.log('test', ''); // 现在正常运行
关键修改点
- 简化配置接口:移除
NoInfer和独立的UpdatedLoggerOptions,让TypeScript能直接从customLevels的键名自动推导泛型类型。 - 固定返回类型:强制
updateConfig返回Logger<C | L>,避免分支逻辑导致的联合类型问题。 - 优化配置合并:处理
level的默认值(新配置未传时沿用原级别),同时确保合并后的customLevels类型正确。
修改后,newLogger的类型会正确推导为Logger<"debug" | "trace" | "test">,调用log('test')不再报错。
内容的提问来源于stack exchange,提问作者Riccardo Perego
相关产品推荐
相关产品推荐

