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

寻求TypeScript类公共属性类型冗余的优化解决方案

类多参数传入的优化方案

通常,当类拥有多个参数时,我们会传入一个对象。下面先梳理三种常见实现方式的问题,再给出更简洁的解决方案。

方案一:嵌套选项对象

这种方式将所有参数封装到一个options对象中,实现简单,但访问属性时必须嵌套调用:

interface CalendarOptions {
  calendarDate?: Date | string,
  todaysDate?: Date | string,
  weeklyAvailability?: {[key: string]: string[]},
  startOfWeek?: number,
  weekdayNames?: string[],
  todayAvailable?: boolean,
  showMonths?: number,
  disablePrevMonths?: boolean,
  events?: {start: Date, end: Date}[],
}

const defaultCalendarOptions: CalendarOptions = {
  calendarDate: new Date(),
  todaysDate: new Date(),
  weeklyAvailability: {},
  startOfWeek: 0,
  weekdayNames: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
  todayAvailable: false,
  showMonths: 2,
  disablePrevMonths: false,
  events: []
}

class Calendar  {
  options: Required<CalendarOptions>
  constructor (options: CalendarOptions = defaultCalendarOptions) {
    this.options = {...defaultCalendarOptions, ...options}
  }

  get example () {
    // 必须通过 this.options.startOfWeek 访问
  }
}

使用时需要嵌套访问属性:

const x = new Calendar({ startOfWeek: 2 })
x.options.startOfWeek

问题:无法在类的顶层直接访问选项属性,代码冗余且不够直观。

方案二:解构选项并绑定到实例属性

这种方式将选项解构后直接赋值给实例属性,实现顶层访问,但需要重复定义类型和默认值:

class Calendar  {
  calendarDate: NonNullable<CalendarOptions['calendarDate']>
  todaysDate: NonNullable<CalendarOptions['todaysDate']>
  weeklyAvailability: NonNullable<CalendarOptions['weeklyAvailability']>
  startOfWeek: NonNullable<CalendarOptions['startOfWeek']>
  weekdayNames: NonNullable<CalendarOptions['weekdayNames']>
  todayAvailable: NonNullable<CalendarOptions['todayAvailable']>
  showMonths: NonNullable<CalendarOptions['showMonths']>
  disablePrevMonths: NonNullable<CalendarOptions['disablePrevMonths']>
  events: NonNullable<CalendarOptions['events']>
  
  constructor (options: CalendarOptions = {}) {
    const {
      calendarDate = new Date(),
      todaysDate = new Date(),
      weeklyAvailability = {},
      startOfWeek = 0,
      weekdayNames = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
      todayAvailable = false,
      showMonths = 2,
      disablePrevMonths = false,
      events = []
    } = options
    this.calendarDate = calendarDate
    this.todaysDate = todaysDate
    this.weeklyAvailability = weeklyAvailability
    this.startOfWeek = startOfWeek
    this.weekdayNames = weekdayNames
    this.todayAvailable = todayAvailable
    this.showMonths = showMonths
    this.disablePrevMonths = disablePrevMonths
    this.events = events
  }
}

问题:类型定义和默认值重复编写,代码量巨大,维护成本高。

方案三:构造器参数+静态工厂方法

这种方式通过构造器定义必填参数(带默认值),并提供静态方法支持对象传入,但需要注意参数顺序:

class Calendar {
  constructor (
    /** 当前显示的日历月份 */
    public calendarDate: Date | string = new Date(),
    /** 独立于日历显示的今日日期 */
    public todaysDate: Date | string = new Date(),
    /** 一周内可预约的时间段 */
    public weeklyAvailability: {[key: string]: string[]} = {},
    /** 一周的起始日,默认周日(0) */
    public startOfWeek: number = 0,
    /** 周几的缩写名称 */
    public weekdayNames: string[] = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
    /** 是否允许预约今日 */
    public todayAvailable: boolean = false,
    /** 可提前查看的月份数,默认2 */
    public showMonths: number = 2,
    /** 是否禁用当前日历日期之前的月份 */
    public disablePrevMonths: boolean = false,
    /** 日历中的事件或不可预约时间段 */
    public events: {start: Date, end: Date}[] = []
  ) {}
 
  static fromInput ({
    calendarDate,
    todaysDate,
    weeklyAvailability,
    startOfWeek,
    weekdayNames,
    todayAvailable,
    showMonths,
    disablePrevMonths,
    events,
  }: {
    calendarDate?: Calendar['calendarDate'],
    todaysDate?: Calendar['todaysDate'],
    weeklyAvailability?: Calendar['weeklyAvailability'],
    startOfWeek?: Calendar['startOfWeek'],
    weekdayNames?: Calendar['weekdayNames'],
    todayAvailable?: Calendar['todayAvailable'],
    showMonths?: Calendar['showMonths'],
    disablePrevMonths?: Calendar['disablePrevMonths'],
    events?: Calendar['events'],
  }): Calendar {
    return new Calendar(
      calendarDate,
      todaysDate,
      weeklyAvailability,
      startOfWeek,
      weekdayNames,
      todayAvailable,
      showMonths,
      disablePrevMonths,
      events,
    )
  }
}

问题:静态方法的参数类型需要重复引用类属性类型,且构造器参数顺序容易出错。


优化方案:利用TypeScript类型工具减少重复

我们可以通过Required和Partial区分外部输入和内部必填类型,同时借助Object.assign将合并后的选项直接挂载到实例,避免重复赋值:

// 外部输入的可选类型
interface CalendarInputOptions {
  calendarDate?: Date | string,
  todaysDate?: Date | string,
  weeklyAvailability?: {[key: string]: string[]},
  startOfWeek?: number,
  weekdayNames?: string[],
  todayAvailable?: boolean,
  showMonths?: number,
  disablePrevMonths?: boolean,
  events?: {start: Date, end: Date}[],
}

// 内部必填的类型(自动推导)
type CalendarRequiredOptions = Required<CalendarInputOptions>

const defaultOptions: CalendarRequiredOptions = {
  calendarDate: new Date(),
  todaysDate: new Date(),
  weeklyAvailability: {},
  startOfWeek: 0,
  weekdayNames: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
  todayAvailable: false,
  showMonths: 2,
  disablePrevMonths: false,
  events: []
}

// 让类实现必填类型,确保所有属性都被定义
class Calendar implements CalendarRequiredOptions {
  calendarDate: Date | string
  todaysDate: Date | string
  weeklyAvailability: {[key: string]: string[]}
  startOfWeek: number
  weekdayNames: string[]
  todayAvailable: boolean
  showMonths: number
  disablePrevMonths: boolean
  events: {start: Date, end: Date}[]

  constructor(options: CalendarInputOptions = {}) {
    // 合并默认值和输入选项,直接赋值给实例
    Object.assign(this, {...defaultOptions, ...options})
  }
}

优势:

  1. 类型复用:通过Required<CalendarInputOptions>自动推导内部必填类型,无需重复定义
  2. 顶层访问:所有属性直接挂载到实例,可通过this.calendarDate或instance.calendarDate直接访问
  3. 代码简洁:通过Object.assign一次性完成属性赋值,避免重复的this.xxx = xxx代码
  4. 类型安全:类实现CalendarRequiredOptions,确保不会遗漏任何属性

使用方式:

const calendar = new Calendar({ startOfWeek: 1 })
console.log(calendar.startOfWeek) // 直接访问,无需嵌套

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 12:05:34