TypeScript类私有属性与公共属性对象传入报错问题咨询
问题场景
我定义了FileTranslatedSuccess类,内部属性newFilePath、newFileName、targetLanguage均为private修饰,构造函数接收包含对应公共属性的对象来初始化这些私有成员。但在另一个类中,当我传入FileTranslatedSuccess实例作为参数创建新实例时,触发了TS2345错误:类型'FileTranslatedSuccess'无法赋值给类型'{newFilePath: string, newFileName: string, targetLanguage: LanguageCode}',属性'newFilePath'在'FileTranslatedSuccess'中是私有的,但在目标类型中不是。
相关代码:
export class FileTranslatedSuccess { private newFilePath!: string; private newFileName!: string; private targetLanguage!: LanguageCode; constructor(object: {newFilePath: string, newFileName: string, targetLanguage: LanguageCode}) { this.newFilePath = object.newFilePath; this.newFileName = object.newFileName; this.targetLanguage = object.targetLanguage; } }
调用代码:
constructor(object: { fileTranslatedSuccess: {[key: string]: FileTranslatedSuccess[]}, fileTranslatedFailed: {originalFile: string, targetLanguage: LanguageCode, message: string}[] | "" }) { let keys = Object.keys(object.fileTranslatedSuccess); this.fileTranslatedSuccess = {}; keys.forEach(key => { this.fileTranslatedSuccess[key] = []; object.fileTranslatedSuccess[key].forEach(fileTranslatedSuccess => { // 错误指向此行 this.fileTranslatedSuccess[key].push(new FileTranslatedSuccess(fileTranslatedSuccess)); }); }); // ... 其余代码 }
核心原因:私有属性导致的类型隔离
TypeScript的类型系统中,私有属性会让类实例的类型和普通结构类型产生本质区别:
- 构造函数期望接收的是一个具有公共属性的结构类型(
{newFilePath: string, ...}),TypeScript会检查传入对象是否在结构上拥有这些公共属性。 - 而
FileTranslatedSuccess实例的newFilePath等属性是私有成员,在类外部的类型检查中,这些私有属性是“不可见”的——TypeScript不会把实例的私有属性当作公共结构类型要求的属性来匹配,因此判定实例类型不符合构造函数的参数类型要求。
即使运行时能通过fileTranslatedSuccess.newFilePath访问私有属性(取决于编译目标),TypeScript的静态类型检查也会在编译阶段阻止这种行为,因为私有属性的设计初衷就是限制仅类内部可访问。
解决方法
方法1:添加实例转普通对象的方法
给FileTranslatedSuccess添加方法,返回包含所需公共属性的普通对象,匹配构造函数的参数要求:
export class FileTranslatedSuccess { private newFilePath!: string; private newFileName!: string; private targetLanguage!: LanguageCode; constructor(object: {newFilePath: string, newFileName: string, targetLanguage: LanguageCode}) { this.newFilePath = object.newFilePath; this.newFileName = object.newFileName; this.targetLanguage = object.targetLanguage; } toPlainObject() { return { newFilePath: this.newFilePath, newFileName: this.newFileName, targetLanguage: this.targetLanguage }; } }
调用时修改为:
this.fileTranslatedSuccess[key].push(new FileTranslatedSuccess(fileTranslatedSuccess.toPlainObject()));
方法2:重载构造函数,支持直接接收类实例
修改构造函数,允许传入FileTranslatedSuccess实例作为参数,利用同类可访问私有属性的特性直接复制:
export class FileTranslatedSuccess { private newFilePath!: string; private newFileName!: string; private targetLanguage!: LanguageCode; constructor(object: {newFilePath: string, newFileName: string, targetLanguage: LanguageCode} | FileTranslatedSuccess) { if (object instanceof FileTranslatedSuccess) { // 同类内部可访问私有属性,直接复制 this.newFilePath = object.newFilePath; this.newFileName = object.newFileName; this.targetLanguage = object.targetLanguage; } else { this.newFilePath = object.newFilePath; this.newFileName = object.newFileName; this.targetLanguage = object.targetLanguage; } } }
这种方式无需修改调用代码,直接传入实例即可正常工作。
方法3:调整属性访问修饰符(不推荐)
如果业务场景不需要严格的私有封装,可将属性改为protected(允许子类访问)或去掉修饰符(变为公共属性),让实例匹配构造函数的结构类型要求。但这种方式会破坏类的封装性,仅在确有必要时使用。
内容的提问来源于stack exchange,提问作者Maestro

