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