Home > Blockchain >  How do I type a class argument that's dependent on the value of another class argument?
How do I type a class argument that's dependent on the value of another class argument?

Time:03-29

In the following class, there is a link between the props method and payload. For example, if method is equal to sendTransaction then payload must be a SendTransactionRequest class.

How do I enforce this with types?

export enum RequestMethod {
  SendTransaction = 'sendTransaction',
  RequestAccounts = 'requestAccounts',
  Other = 'other',
  // ...
}

interface RequestProps {
  id: number;
  method: RequestMethod;
  payload: ???;
}

export class Request {
  private constructor(props: RequestProps)
    this.props = Object.freeze(props);
  }

  public getID(): number {
    return this.props.id;
  }

  public getMethod(): RequestMethod {
    return this.props.method;
  }

  public getPayload(): ??? {
    return this.props.payload;
  }

  public static create(props: RequestProps): Request {
    return new Request(props);
  }
}

CodePudding user response:

You should use typescript type for this purpose.

You must enforce some limited allowed combination of types as follows:

type SendTransactionRequest = string;
type RequestAccountsRequest = number;

type RequestMethod = "SendTransaction" | "RequestAccounts" | "Other";

type RequestProps = {
  id: number;
} & (
  | {
      method: "SendTransaction";
      payload: SendTransactionRequest;
    }
  | {
      method: "RequestAccounts";
      payload: RequestAccountsRequest;
    }
);

With this type definition, all RequestProps will have an id, but there are only certain allowed combinations of method and payload allowed.

// valid
const m1: RequestProps = {
    id: 0,
    method: 'RequestAccounts',
    payload: 0,
}

// valid
const m2: RequestProps = {
    id: 0,
    method: 'SendTransaction',
    payload: '',
}

// error: Type 'string' is not assignable to type 'number'
const m3: RequestProps = {
    id: 0,
    method: 'RequestAccounts',
    payload: '',
}

// error: Type 'number' is not assignable to type 'string'
const m4: RequestProps = {
    id: 0,
    method: 'SendTransaction',
    payload: 0,
}

If you then what to type the Request class to have the correct return type for getPayload() you need to use generics:

export class Request<Props extends RequestProps = RequestProps> {
  props: Props;

  private constructor(props: Props) {
    this.props = Object.freeze(props);
  }

  public getID(): number {
    return this.props.id;
  }

  public getMethod(): RequestMethod {
    return this.props.method;
  }

  public getPayload(): Props["payload"] {
    return this.props.payload;
  }

  public static create<Props extends RequestProps>(
    props: Props
  ): Request<Props> {
    return new Request(props);
  }
}

const req = Request.create({
  id: 0,
  method: "SendTransaction",
  payload: "",
});

const payload = req.getPayload() // string
  • Related