如何避免重复定义类的类型与构造函数传入属性?
将JavaScript Token类迁移至TypeScript时的DRY实现方案
问题描述
原JavaScript类通过Object.assign直接接收传入对象初始化实例,依赖调用者保证属性正确性:
export class Token { constructor(obj) { Object.assign(this, obj) } // 其他方法 }
迁移到严格模式TypeScript后,最初的实现存在TokenProps接口与类属性的重复定义,违背DRY原则:
interface TokenProps { index: number length: number op: string phrase: string quotes: 0 | 1 | 2 wild: '' | '*' } export class Token { index: number length: number op: string phrase: string quotes: 0 | 1 | 2 wild: '' | '*' constructor(obj: TokenProps) { this.index = obj.index this.length = obj.length this.op = obj.op this.phrase = obj.phrase this.quotes = obj.quotes this.wild = obj.wild } // 其他方法 }
当前尝试的方案
目前的写法避免了重复,但使用非空断言(!),担心不够严谨:
class TokenBase { index!: number length!: number op!: string phrase!: string quotes!: 0 | 1 | 2 wild!: '' | '*' } type TokenProps = {[k in keyof TokenBase]: TokenBase[k]} export class Token extends TokenBase { constructor(obj: TokenProps) { super() Object.assign(this, obj) } }
优化方案与解释
方案1:简化当前实现,保留非空断言
你的当前方案其实是合理的,非空断言在这里是安全的:
TokenBase的属性用!标记,是因为TypeScript无法感知子类构造函数会通过Object.assign完成初始化- 实际运行时,
Token的构造函数会立即给所有属性赋值,不存在未初始化的情况
可以进一步简化TokenProps的定义,直接复用TokenBase的实例类型:
class TokenBase { index!: number length!: number op!: string phrase!: string quotes!: 0 | 1 | 2 wild!: '' | '*' } type TokenProps = TokenBase export class Token extends TokenBase { constructor(obj: TokenProps) { super() Object.assign(this, obj) } // 其他方法 }
方案2:利用接口+Object.assign减少重复
如果不想使用基类,也可以通过接口定义属性,类实现接口后用Object.assign简化构造函数:
interface TokenProps { index: number length: number op: string phrase: string quotes: 0 | 1 | 2 wild: '' | '*' } export class Token implements TokenProps { index: number length: number op: string phrase: string quotes: 0 | 1 | 2 wild: '' | '*' constructor(obj: TokenProps) { Object.assign(this, obj) } // 其他方法 }
虽然类仍需声明属性,但TypeScript会强制属性与接口保持一致,且构造函数无需逐个赋值。
方案3:参数属性+解构赋值(适合属性较少的场景)
如果属性数量不多,可以用参数属性结合解构赋值,一次性完成属性声明与初始化:
interface TokenProps { index: number length: number op: string phrase: string quotes: 0 | 1 | 2 wild: '' | '*' } export class Token implements TokenProps { constructor({ index, length, op, phrase, quotes, wild }: TokenProps) { this.index = index this.length = length this.op = op this.phrase = phrase this.quotes = quotes this.wild = wild } // 其他方法 }
这种方式无需额外的基类或断言,但仍存在接口与解构参数的重复。
内容的提问来源于stack exchange,提问作者Aron Griffis
相关产品推荐
相关产品推荐

