0

I have the following C# class:

public class Envelope<T> {

  public List<Error> Errors { get; private set; } = new List<Error>();

  public Paging Paging { get; private set; }

  public List<T> Result { get; private set; } = new List<T>();


  public Envelope(T result) : this(new List<T> { result }, null, new List<Error>()) { }

  public Envelope(List<T> result) : this (result, null, new List<Error>()) { }

  public Envelope(List<Error> errors) : this(new List<T>(), null, errors) { }

  public Envelope(List<T> result, Paging paging, List<Error> errors) {

    Errors = errors;
    Paging = paging;
    Result = result;

  }

}

I need to convert this class to TypeScript on an Angular 6 project so I did:

export class Envelope<T> {

  errors: Error[];
  paging: Paging;
  result: T[];

  constructor(result: T[], paging: Paging, errors: Error[]) {

    this.errors = errors;
    this.paging = paging;
    this.result = result;

  }

}

The problem is that Typescript does not allow more than one constructor so replicating the quite different constructors I have in C# seems impossible.

Is there a way to do this?

Should Envelope be an interface in TypeScript?

Basically Envelope is a Wrapper for an API response to contain various objecs such as the Result itself, Paging and List of possible errors.

1
  • What about using nullable values? Commented Nov 6, 2018 at 11:34

2 Answers 2

4

You can create constructor overloads, but you have to distinguish between them manually in the implementation. In your case, the first argument for the implementation would be a union of T| T[] | Error[] and you can use type guards to manually differentiate between the cases in the union:

function isErrorArray<T>(e: T | Error[]): e is Error[] {
    return e instanceof Array && e[0] && e[0] instanceof Error;
}
export class Envelope<T> {

    errors: Error[];
    paging: Paging;
    result: T[];

    constructor(result: T)
    constructor(errors: Error[])
    constructor(result: T[], paging: Paging, errors: Error[])
    constructor(result: T | T[] | Error[], paging?: Paging, errors?: Error[]) {
        if (isErrorArray(result)) {
            errors = result;
            result = [] as T[];
        }
        if (Array.isArray(result)) {

        } else {
            result = [result];
        }
        this.errors = errors;
        this.paging = paging;
        this.result = result; // result will be T[] because of the above ifs

    }

}
Sign up to request clarification or add additional context in comments.

Comments

3

Given your code, this should work and seems waaaay simpler :

export class Envelope<T> {
  constructor(
    public result?: T[], 
    public paging?: Paging, 
    public errors: Error[] = null
  ) {}
}

You have shorthands :

  • an access modifier appended before a constructor parameter will declare a class member. So you don't need to instanciate your class members in your constructor.
  • Using a ? after a member name renders it optional, meaning that if you don't provide it, you'll simply have undefined in place of it's value.
  • Setting a value in the parameters of a function (or a constructor) renders the member optional and gives it a default value.

As a side note, Typescript allow several constructors in a ... strange way :

export class Envelope<T> {
  constructor(public result: T[])
  constructor(public paging: Paging)
  constructor(public errors: Error[])
  // ...
  {}
}

The issue with this syntax is that you will have to test every paramter with instanceof to see if they're of the expected type, which I don't particularily like (optional parameters seems simpler to me)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.