export declare type ReceiptEnum = {
Action: {
actions: Action[]; // Action is a type too. Consider it as a string for the ex.
gasPrice: string;
inputDataIds: string[];
outputDataReceivers: string[];
signerId: string;
signerPublicKey: string;
};
} | {
Data: {
data: string;
dataId: string;
};
};
I have this above code and I want to cast something which will be of type
const data : ReceiptEnum['Action'] = {}; // This doesn't work
How would I do that in TS? Doing this currentlt throws an error Property 'Action' does not exist on type 'ReceiptEnum'
You cannot refer to a property of a union like that. You will need to declare separate types for them:
interface ReceiptAction {
actions: Action[];
gasPrice: string;
inputDataIds: string[];
outputDataReceivers: string[];
signerId: string;
signerPublicKey: string;
}
interface ReceiptData {
data: string;
dataId: string;
}
export declare type ReceiptEnum = {
Action: ReceiptAction
} | {
Data: ReceiptAction;
};
Then you can directly refer to just ReceiptAction.
TypeScript does, as far as I'm concerned, not support enums containing struct like data (unlike rust, for example) which seems you are trying to recreate here. See the comments
You can't index the type like that. TypeScript does not have1 any information about the union variants until you check which one it is. That's why you can't index it. And even if you could, the resulting type would not be assignable to ReceiptEnum.
But you may still construct your existing type the following way:
const data: ReceiptEnum = {
Action: {
gasPrice: "",
inputDataIds: [],
outputDataReceivers: [],
signerId: "",
signerPublicKey: "",
},
};
But I normally use a separate field (e.g. type) to distinguish union variants. It's a pretty common pattern in TypeScript to the extent of my knowledge.
export enum ReceiptType {
Action,
Data,
}
export interface ActionReceipt {
type: ReceiptType.Action,
actions: Action[];
gasPrice: string;
inputDataIds: string[];
outputDataReceivers: string[];
signerId: string;
signerPublicKey: string;
}
export interface DataReceipt {
type: ReceiptType.Data,
data: string;
dataId: string;
}
export type Receipt = ActionReceipt | DataReceipt;
// Usage:
const receipt: Receipt = {
type: ReceiptType.Action,
actions: [],
gasPrice: "",
inputDataIds: [],
outputDataReceivers: [],
signerId: "",
signerPublicKey: "",
};
processReceipt(receipt);
function processReceipt(receipt: Receipt) {
if (receipt.type == ReceiptType.Action) {
return processActionReceipt(receipt);
}
throw new Error(`processing of receipt type ${receipt.type} not implemented`);
}
function processActionReceipt(receipt: ActionReceipt) {}
Well it makes a lot of sense that TS is throwing that error at you. You're declaring that data is of type Action despite Action having a number of mandatory fieldsthat don't exist in the empty object that is assigned todata`.
If the goal here is to pretend that data is already a valid Action type then what we want to do is something like this:
const data = {} as ReceiptEnum['Action'];
What I recommend you do instead of this though is have all of the fields ready to go ahead of time and then create the Action without pretending as if data already contains the fields that we expect it to contain. So maybe something like this instead:
const actions = alreadyPreparedActions;
const gasPrice = '$5.00';
const inputDataIds = ['a', 'b', 'c'];
const outputDataReceivers = ['d', 'e', 'f'];
const signerId = 'super signer!';
const signerPublicKey = 'ghi';
const data: ReceiptEnum['Action'] = {
actions,
gasPrice,
inputDataIds,
outputDataReceivers,
signerId,
signerPublicKey,
};