1

I am trying to write a typescript class with private properties, but these properties are set in constructor, the constructor needs only an object, but I always get an error, that "error TS2345: Argument of type 'TranslateResponse' is not assignable to parameter of type '{ status: "success" | "failed"; errorCode?: number | undefined; message?: string | undefined; data?: any; }'. 2023-05-30 13:45:24 translator-angular | Property 'status' is private in type 'TranslateResponse' but not in type '{ status: "success" | "failed"; errorCode?: number | undefined; message?: string | undefined; data?: any; }'. I don't understand, what meaning does it have, if an object's property is private, or public? I read some stories about that, but they all were issues with interfaces, but I don't have any interface.

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;
    }

And that constructor gets an object here:

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));
            });
        });
        if (object.fileTranslatedFailed === "") {
            this.fileTranslatedFailed = "";
        } else {
            this.fileTranslatedFailed = [];
            object.fileTranslatedFailed.forEach(fileTranslatedFailed => {
                if (Array.isArray(this.fileTranslatedFailed)) {
                    this.fileTranslatedFailed.push(new FileTranslatedFailed(fileTranslatedFailed));
                }
            });
        }
    }

That is the constructor of an other object. The error is pointing on the instantiating of FileTranslatedSuccess class. ( this.fileTranslatedSuccess[key].push(new FileTranslatedSuccess(fileTranslatedSuccess)); ). Could someone explain to me, why is it an error, if I set private properties in constructor from an object with public properties?

Maestro
  • 43
  • 1
  • 6
  • So all you're asking here is why it's an error? That's because if it were allowed, then you could easily access private properties from outside the class, as shown [here](https://tsplay.dev/wg8MBN). Does that fully address the question? If so I'll write up an answer explaining; if not, what am I missing? – jcalz May 30 '23 at 13:28

1 Answers1

0
object.fileTranslatedSuccess[key].forEach(fileTranslatedSuccess => {
    this.fileTranslatedSuccess[key].push(new FileTranslatedSuccess(fileTranslatedSuccess));
});

Typescript assumes fileTranslatedSuccess is of type FileTranslatedSuccess. Typescript does not read the real-time value of fileTranslatedSuccess during compilation and assumes only the public properties within FileTranslatedSuccess class are available. Even if the value of fileTranslatedSuccess has valid and public values of newFilePath, newFileName, targetLanguage during runtime, Typescript does not know nor care.

You have confused the TypeScript compiler. In one sense you are stating that these 3 properties should be hidden from public view but in the next you are requesting public view so that you can assign the values from one instance to the next.

To fix this issue you can

  1. Make the properties public

  2. Make getter functions to return the values for each property. Something like

     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;
     }
    
     getFilePath(): string {
         return this.newFilePath;
     }
    
     getFileName(): string {
         return this.newFileName;
     }
    
     getTargetLanguage(): string {
         return this.targetLanguage;
     }
    

    }

then in your call you can do something like

object.fileTranslatedSuccess[key].forEach(fileTranslatedSuccess => {
    const filePath = fileTranslatedSuccess.getFilePath();
    const fileName = fileTranslatedSuccess.getFileName();
    const targetLanguage = fileTranslatedSuccess.getTargetLanguage();
    
    this.fileTranslatedSuccess[key].push(new FileTranslatedSuccess({newFilePath: filePath, newFileName: fileName, targetLanguage: targetLanguage}));
});
  1. If object.fileTranslatedSuccess are from an API call and you are trying to instantiate an Object as type FileTranslatedSuccess, then then using interfaces may be in your best interest. Make the 3 properties public in the interface then assign as normal in your constructor. This will help with clarity with anyone else working on your project. Here is a basic example (not tested).

     export interface IFileTranslatedSuccess {
         newFilePath: string;
         newFileName: string;
         targetLanguage: LanguageCode;
     }
    
    
     object.fileTranslatedSuccess[key].forEach((ifileTranslatedSuccess: IFileTranslatedSuccess) => {     
         this.fileTranslatedSuccess[key].push(new FileTranslatedSuccess(ifileTranslatedSuccess));
     });
    

then in your FileTranslatedSuccess class,

export class FileTranslatedSuccess {
    
    private newFilePath!: string;
    private newFileName!: string;
    private targetLanguage!: LanguageCode;

    constructor(object: IFileTranslatedSuccess) {
       this.newFilePath = object.newFilePath;
       this.newFileName = object.newFileName;
       this.targetLanguage = object.targetLanguage;
       ...
    }
}
Esaith
  • 706
  • 9
  • 12
  • Thank you! I will try it, but ny the way I have getter methods for all the properties. I just did not paste them with the code. That would be the point, I want to set the properties by constructor, but then all the properties are only gettable, but not settable. – Maestro Jun 02 '23 at 11:38