0

I have a Java class which class I want to use in typescript project. But I tried to convert it and took help from http://www.jsweet.org/jsweet-live-sandbox/ also. I am very new in typescript, I am a java developer and currently learning fronted language. So I am facing problem to identify the error. It will very helpful if you help me to fix my typescript code.

Here is my Java class:

import bd.edu.seu.erp.model.exception.IncorrectSemesterError;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class Semester {
    private static final String NAMES[] = {"Spring", "Summer", "Fall"};
    private static final int OFFSET = 2002;

    private static Map<Integer, Semester> semesterMap = new HashMap<>();
    private static Map<String, Semester> semesterNameMap = new HashMap<>();

    private int semesterNumber;
    private String label;

    public Semester(int semesterNumber, String label) {
        this.semesterNumber = semesterNumber;
        this.label = label;
    }

    private Semester(int semesterNumber) {
        if (semesterNumber < 1)
            throw new IncorrectSemesterError("Invalid Semester", "Semester number cannot be less than 1");
        this.semesterNumber = semesterNumber;
        this.label = NAMES[semesterNumber % NAMES.length] + " " + (semesterNumber / 3 + OFFSET);
    }

    private Semester(String semesterName) {
        this.label = semesterName;
        String[] tokens = semesterName.split("\\ ");
        if (tokens.length != 2)
            throw new IncorrectSemesterError("Invalid Semester", "Semester label has incorrect number of tokens");
        String name = tokens[0];
        int year = Integer.parseInt(tokens[1]);
        if (year < OFFSET)
            throw new IncorrectSemesterError("Invalid Semester", "Year cannot be earlier than " + OFFSET);
        int nameIndex = Arrays.asList(NAMES).indexOf(name);
        if (nameIndex < 0 || nameIndex > NAMES.length)
            throw new IncorrectSemesterError("Invalid Semester", "Name of the semester must be one of [Spring, Summer, Fall]");
        this.semesterNumber = (year - OFFSET) * 3 + nameIndex;
    }

    public static Semester of(int semesterNumber) {
        Semester semester = semesterMap.getOrDefault(semesterNumber, new Semester(semesterNumber));
        semesterMap.putIfAbsent(semester.semesterNumber, semester);
        semesterNameMap.putIfAbsent(semester.label, semester);
        return semester;
    }

    public static Semester of(String semesterName) {
        Semester semester = semesterNameMap.getOrDefault(semesterName, new Semester(semesterName));
        semesterMap.putIfAbsent(semester.semesterNumber, semester);
        semesterNameMap.putIfAbsent(semester.label, semester);
        return semester;
    }

    /*
    public static Semester of(Semester semesterObject) {
        Semester semester = semesterNameMap.getOrDefault(semesterObject.semesterNumber, new Semester(semesterObject.semesterNumber));
        semesterMap.putIfAbsent(semester.semesterNumber, semester);
        semesterNameMap.putIfAbsent(semester.label, semester);
        return semester;
    }
    */
}

Here is my typescript file what I tried:


export class Semester {

    static NAMES: string[] = ['Spring', 'Summer', 'Fall'];
    static OFFSET = 2002;

    static semesterMap: Map<number, Semester> = new Map();
    static semesterNameMap: Map<String, Semester> = new Map();

    private semesterNumber: number;
    private label: String;

    public constructor(semesterNumber?: number, label?: String) {
        if (((typeof semesterNumber === 'number') || semesterNumber === null) && ((typeof label === 'string') || label === null)) {
            if (this.semesterNumber === undefined) {
                this.semesterNumber = 0;
            }
            if (this.label === undefined) {
                this.label = null;
            }
            if (this.semesterNumber === undefined) {
                this.semesterNumber = 0;
            }
            if (this.label === undefined) {
                this.label = null;
            }
            (() => {
                this.semesterNumber = semesterNumber;
                this.label = label;
            })();
        } else if (((typeof label === 'string') || semesterNumber === null) && semesterNumber === undefined) {
            if (this.semesterNumber === undefined) {
                this.semesterNumber = 0;
            }
            if (this.label === undefined) {
                this.label = null;
            }
            if (this.semesterNumber === undefined) {
                this.semesterNumber = 0;
            }
            if (this.label === undefined) {
                this.label = null;
            }
            (() => {
                const tokens: string[] = label.split('\\ ');
                if (tokens.length !== 2) {
                    throw new Error('Semester label has incorrect number of tokens');
                }
                const name: string = tokens[0];
                const year: number = parseInt(tokens[1], 10);
                if (year < Semester.OFFSET) {
                    throw new Error('Year cannot be earlier than ' + Semester.OFFSET);
                }
                const nameIndex: number = Semester.NAMES.slice(0).indexOf(name);
                if (nameIndex < 0 || nameIndex > Semester.NAMES.length) {
                    throw new Error('Name of the semester must be one of [Spring, Summer, Fall]');
                }
                this.semesterNumber = (year - Semester.OFFSET) * 3 + nameIndex;
            })();
        } else if (((typeof semesterNumber === 'number'))) {
            if (this.semesterNumber === undefined) {
                this.semesterNumber = 0;
            }
            if (this.label === undefined) {
                this.label = null;
            }
            if (this.semesterNumber === undefined) {
                this.semesterNumber = 0;
            }
            if (this.label === undefined) {
                this.label = null;
            }
            (() => {
                if (semesterNumber < 1) {
                    throw new Error('Semester number cannot be less than 1');
                }
                this.semesterNumber = semesterNumber;
                this.label = Semester.NAMES[semesterNumber % Semester.NAMES.length] + ' ' + (semesterNumber / 3 + Semester.OFFSET);
            })();
        } else {
            throw new Error('invalid overload');
        }
    }

    public static of(semesterNumber?: number, semesterName?: string): Semester {
        if (typeof semesterNumber === 'number') {
            const semester: Semester = Semester.semesterMap.getOrDefault(semesterNumber, new Semester(semesterNumber));
            Semester.semesterMap.putIfAbsent(semester.semesterNumber, semester);
            Semester.semesterNameMap.putIfAbsent(semester.label, semester);
            return semester;
        }
        if (typeof semesterName === 'string') {
            const semester: Semester = Semester.semesterNameMap.getOrDefault(semesterName, new Semester(semesterName));
            Semester.semesterMap.putIfAbsent(semester.semesterNumber, semester);
            Semester.semesterNameMap.putIfAbsent(semester.label, semester);
            return semester;
        }
    }
}

First of all, I have confusion in typescript constructor. I am not sure, is it the correct way what I wrote for multiple constructor in java. Second, in typescript I didn't find getOrDefault, putIfAbsent in Map. I didn't understand what should write here.

Can you please help me to fix this typescript code? Thanks in advance.

John Doe
  • 3
  • 1

2 Answers2

1

Regarding the constructor

You guessed right, there are no multiple constructors like you are used to have in Java (aka. constructor overloading). You only have one constructor in TypeScript and you need to have optional parameters.

In your case, semesterNumber and label could be either undefined or number/string, no need for a type check.

The way to check for "what got passed in" you don't need so many checks in one if-statement. Have a look on the following answer here: https://stackoverflow.com/a/44017547/8745384

Regarding the map

The equivalent of "putIfAbsent" is a simple "set(key, value)"

Semester.semesterMap.set(semester.semesterNumber, semester);

For getOrDefault you could write

const semester: Semester = Semester.semesterMap.get(semesterNumber) || new Semester(semesterNumber);

It does the same as I described for the if-statement before. Check if null OR undefined OR empty if that's the case execute new Semester(...).

Hope I could help. I recently switched from Java to TypeScript too, so maybe there is a better way of doing then I described.

Florian Schlag
  • 639
  • 4
  • 16
1

I have a Java background too, but older. ;-)

Some advices:

  • Choose undefined or null but you shouldn't use both. In the example below I use undefined;
  • In modules, static members can be replaced with simple variables;
  • Prefer interface over multiple optional parameters;
  • Your IIFE ((() => { … }();) are useless;
  • Do not declare a type when inference is enough;
  • Prefer primitive types (string instead of String).

Here is an example:

const NAMES = ['Spring', 'Summer', 'Fall'];
const OFFSET = 2002;

const numberMap = new Map<number, Semester>();
const labelMap = new Map<string, Semester>();

export interface SemesterOptions {
    semesterNumber?: number
    label?: string
}

export class Semester {
    private semesterNumber: number;
    private label: string;

    constructor({ semesterNumber, label }: SemesterOptions) {
        if (semesterNumber === undefined) {
            if (label === undefined) {
                throw new Error('invalid options');
            }
            const parsed = parseLabelToSemesterOptions(label);
            semesterNumber = parsed.semesterNumber;
            label = parsed.label;
        } else if (label === undefined) {
            if (semesterNumber < 1) {
                throw new Error('Semester number cannot be less than 1');
            }
            label = NAMES[semesterNumber % NAMES.length] + ' ' + (semesterNumber / 3 + OFFSET);
        }

        this.semesterNumber = semesterNumber;
        this.label = label;
    }


    static ofNumber(semesterNumber: number): Semester {
        let semester = numberMap.get(semesterNumber);
        if (!semester) {
            semester = new Semester({ semesterNumber });
            numberMap.set(semester.semesterNumber, semester);
            if (!labelMap.has(semester.label)) {
                labelMap.set(semester.label, semester);
            }
        }
        return semester; 
    }

    static ofLabel(label: string): Semester {
        let semester = labelMap.get(label);
        if (!semester) {
            semester = new Semester({ label });
            labelMap.set(semester.label, semester);
            if (!numberMap.has(semester.semesterNumber)) {
                numberMap.set(semester.semesterNumber, semester);
            }
        }
        return semester; 
    }
}

function parseLabelToSemesterOptions(labelToParse: string): Required<SemesterOptions> {
    const tokens = labelToParse.split('\\ ');
    if (tokens.length !== 2) {
        throw new Error('Semester label has incorrect number of tokens');
    }
    const label = tokens[0];
    const year = parseInt(tokens[1], 10);
    if (year < OFFSET) {
        throw new Error('Year cannot be earlier than ' + OFFSET);
    }
    const nameIndex = NAMES.indexOf(label);
    if (nameIndex === -1) {
        throw new Error(`Name of the semester must be one of ${NAMES.join(', ')}`);
    }
    const semesterNumber = (year - OFFSET) * 3 + nameIndex;
    return {
        semesterNumber,
        label
    }
}

For getOrDefault: use get and test if the result is undefined (or falsy). There is a shortcut with a test on falsy:

const val = myMap.get(myKey) || myDefautValue;

… but in your case a if statement is more appropriate.

For putIfAbsent: use has first to test if the key already exists, then use set if necessary.

Paleo
  • 21,831
  • 4
  • 65
  • 76