You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript中如何让Logger构造函数默认属性在log方法中可选?

实现动态必填项的Logger类

我有一个Logger类,构造函数可以接收可选的默认配置:

interface Log {
  level: string
  scope: string
  source: string
  message: string
}

type LogDefaults = Partial<Log>
type LogInput = Log

class Logger {
  constructor(private defaults: LogDefaults = {}) {}

  public log(log: LogInput): void {
    console.log({ ...this.defaults, ...log })
  }
}

我希望构造函数传入的defaults属性,在log方法中自动变为可选属性。能不能定义LogInput类型,让只有未在LogDefaults中提供的属性才是必填项?

要求以下场景正常工作:

const logger = new Logger()
logger.log({
  scope: 'my-scope',
  source: 'my-souce',
  level: 'info',
  message: 'my message'
})

以下场景也正常工作:

const logger = new Logger({ scope: 'my-scope', source: 'my-souce' })
logger.log({ 
    level: 'info',
    message: 'my message'
})

以下场景应该报错(缺少必填的source属性):

const logger = new Logger({ scope: 'my-scope' })
logger.log({ 
    level: 'info',
    message: 'my message'
})

解决方案:用泛型约束实现动态必填项

要实现这个需求,需要把Logger改为泛型类,让构造函数的defaults类型作为泛型参数,以此精准推导log方法的入参类型。具体实现如下:

interface Log {
  level: string
  scope: string
  source: string
  message: string
}

// 泛型类,D捕获构造时传入的defaults的具体类型
class Logger<D extends Partial<Log>> {
  constructor(private defaults: D) {}

  // 拼接出动态入参类型:未默认的属性必填,已默认的属性可选
  public log(log: Omit<Log, keyof D> & Partial<Pick<Log, keyof D>>): void {
    console.log({ ...this.defaults, ...log })
  }
}

代码说明

  1. 泛型参数D:替代原本宽泛的Partial<Log>,精准记录用户传入的默认属性,让TypeScript知道哪些属性已有默认值。
  2. 动态入参类型:
    • Omit<Log, keyof D>:从Log中排除已设置默认值的属性,这些就是必填项(因为没有默认值兜底)。
    • Partial<Pick<Log, keyof D>>:把已设置默认值的属性转为可选,允许用户在log时覆盖默认值。
    • 两者交叉后,最终入参就会严格遵循「缺啥必填啥」的规则。

场景验证

  1. 无默认值的情况:
const logger = new Logger({})
logger.log({
  scope: 'my-scope',
  source: 'my-souce',
  level: 'info',
  message: 'my message'
})
// 正常:所有属性都无默认值,必须完整传入
  1. 带部分默认值的情况:
const logger = new Logger({ scope: 'my-scope', source: 'my-souce' })
logger.log({ 
    level: 'info',
    message: 'my message'
})
// 正常:scope和source已有默认值,只需传入剩余必填的level和message
  1. 缺少必填项的情况:
const logger = new Logger({ scope: 'my-scope' })
logger.log({ 
    level: 'info',
    message: 'my message'
})
// 报错:source未设置默认值,属于必填项,此处未传入

额外优化:支持无参数初始化

如果想要保留new Logger()这种无参数写法,可以给构造函数的defaults加默认值,并设置泛型参数的默认值:

class Logger<D extends Partial<Log> = {}> {
  constructor(private defaults: D = {} as D) {}

  public log(log: Omit<Log, keyof D> & Partial<Pick<Log, keyof D>>): void {
    console.log({ ...this.defaults, ...log })
  }
}

// 现在可以直接写:
const logger = new Logger()
logger.log({ /* 所有属性必填 */ })

内容的提问来源于stack exchange,提问作者Renato

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 13:35:03