寻求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}) } }
优势:
- 类型复用:通过
Required<CalendarInputOptions>自动推导内部必填类型,无需重复定义 - 顶层访问:所有属性直接挂载到实例,可通过
this.calendarDate或instance.calendarDate直接访问 - 代码简洁:通过
Object.assign一次性完成属性赋值,避免重复的this.xxx = xxx代码 - 类型安全:类实现
CalendarRequiredOptions,确保不会遗漏任何属性
使用方式:
const calendar = new Calendar({ startOfWeek: 1 }) console.log(calendar.startOfWeek) // 直接访问,无需嵌套
内容的提问来源于stack exchange,提问作者ThomasReggi
相关产品推荐
相关产品推荐

