Type Guards
Description
Section titled “Description”This example demonstrates how to use type guard functions to check argument and type categories in the ABI system:
- argTypeIsTransaction(): Check if type is a transaction type (txn, pay, keyreg, acfg, axfer, afrz, appl)
- argTypeIsReference(): Check if type is a reference type (account, asset, application)
- argTypeIsAbiType(): Check if type is a standard ABI type (not transaction or reference)
- isAVMType(): Check if type is an AVM-specific type (AVMBytes, AVMString, AVMUint64) These guards are essential for:
- Method argument handling and routing
- TypeScript type narrowing for safer code
- Determining how to encode/decode values based on type category
Prerequisites
Section titled “Prerequisites”- No LocalNet required
Run This Example
Section titled “Run This Example”From the repository root:
cd examplesnpm run example abi/13-type-guards.ts/** * Example: Type Guards * * This example demonstrates how to use type guard functions to check * argument and type categories in the ABI system: * * - argTypeIsTransaction(): Check if type is a transaction type (txn, pay, keyreg, acfg, axfer, afrz, appl) * - argTypeIsReference(): Check if type is a reference type (account, asset, application) * - argTypeIsAbiType(): Check if type is a standard ABI type (not transaction or reference) * - isAVMType(): Check if type is an AVM-specific type (AVMBytes, AVMString, AVMUint64) * * These guards are essential for: * - Method argument handling and routing * - TypeScript type narrowing for safer code * - Determining how to encode/decode values based on type category * * Prerequisites: * - No LocalNet required */
import type { ABIMethodArgType } from '@algorandfoundation/algokit-utils/abi';import { ABIMethod, ABIReferenceType, ABITransactionType, ABIType, argTypeIsAbiType, argTypeIsReference, argTypeIsTransaction, isAVMType,} from '@algorandfoundation/algokit-utils/abi';import { printHeader, printInfo, printStep, printSuccess } from '../shared/utils.js';
function main() { printHeader('Type Guards Example');
// Step 1: Introduction to type categories printStep(1, 'Introduction to Type Categories');
printInfo('In ABI method calls, arguments can be categorized into:'); printInfo(''); printInfo('1. Transaction types: Represent transaction arguments'); printInfo(' txn, pay, keyreg, acfg, axfer, afrz, appl'); printInfo(''); printInfo('2. Reference types: Represent references to on-chain entities'); printInfo(' account, asset, application'); printInfo(''); printInfo('3. ABI types: Standard ARC-4 encoded types'); printInfo(' uint64, string, address, (tuple), arrays, etc.'); printInfo(''); printInfo('4. AVM types: Native AVM stack value types'); printInfo(' AVMBytes, AVMString, AVMUint64');
// Step 2: argTypeIsTransaction() - Check for transaction types printStep(2, 'argTypeIsTransaction() - Check for Transaction Types');
printInfo('Transaction types identify arguments that must be transactions:'); printInfo('');
const transactionTypes = [ 'txn', // Any transaction 'pay', // Payment transaction 'keyreg', // Key registration 'acfg', // Asset configuration 'axfer', // Asset transfer 'afrz', // Asset freeze 'appl', // Application call ];
for (const type of transactionTypes) { const result = argTypeIsTransaction(type as ABIMethodArgType); printInfo(` argTypeIsTransaction("${type}"): ${result}`); }
printInfo(''); printInfo('Non-transaction types:'); const nonTransactionTypes = ['uint64', 'string', 'account', 'asset', 'application']; for (const type of nonTransactionTypes) { const result = argTypeIsTransaction(type as ABIMethodArgType); printInfo(` argTypeIsTransaction("${type}"): ${result}`); }
// Step 3: argTypeIsReference() - Check for reference types printStep(3, 'argTypeIsReference() - Check for Reference Types');
printInfo('Reference types identify foreign references in method calls:'); printInfo('');
const referenceTypes = [ 'account', // Account reference (goes in accounts array) 'asset', // Asset reference (goes in foreign assets) 'application', // Application reference (goes in foreign apps) ];
for (const type of referenceTypes) { const result = argTypeIsReference(type as ABIMethodArgType); printInfo(` argTypeIsReference("${type}"): ${result}`); }
printInfo(''); printInfo('Non-reference types:'); const nonReferenceTypes = ['txn', 'pay', 'uint64', 'address', 'string']; for (const type of nonReferenceTypes) { const result = argTypeIsReference(type as ABIMethodArgType); printInfo(` argTypeIsReference("${type}"): ${result}`); }
// Step 4: argTypeIsAbiType() - Check for standard ABI types printStep(4, 'argTypeIsAbiType() - Check for Standard ABI Types');
printInfo('ABI types are standard ARC-4 encoded types (not txn or reference):'); printInfo('');
const allTypes = [ 'uint64', 'string', 'address', 'bool', 'byte', 'byte[32]', 'uint64[]', '(uint64,bool)', 'txn', 'pay', 'account', 'asset', 'application', ];
for (const type of allTypes) { // For ABI types, we need the actual ABIType object or string const isAbi = argTypeIsAbiType(type as ABIMethodArgType); printInfo(` argTypeIsAbiType("${type}"): ${isAbi}`); }
printInfo(''); printInfo('Note: argTypeIsAbiType returns true when NOT a transaction AND NOT a reference');
// Step 5: isAVMType() - Check for AVM-specific types printStep(5, 'isAVMType() - Check for AVM-Specific Types');
printInfo('AVM types represent native Algorand Virtual Machine stack values:'); printInfo('');
const avmTypes = ['AVMBytes', 'AVMString', 'AVMUint64'];
for (const type of avmTypes) { const result = isAVMType(type); printInfo(` isAVMType("${type}"): ${result}`); }
printInfo(''); printInfo('Non-AVM types:'); const nonAvmTypes = ['uint64', 'string', 'address', 'bytes', 'txn', 'account']; for (const type of nonAvmTypes) { const result = isAVMType(type); printInfo(` isAVMType("${type}"): ${result}`); }
// Step 6: TypeScript type narrowing with guards printStep(6, 'TypeScript Type Narrowing with Guards');
printInfo('Type guards enable TypeScript type narrowing for safer code:'); printInfo('');
// Demonstrate with an ABIMethodArgType function demonstrateTypeNarrowing(argType: ABIMethodArgType): void { if (argTypeIsTransaction(argType)) { // TypeScript knows argType is ABITransactionType here printInfo(` Transaction type detected: ${argType}`); printInfo(` This arg requires a transaction to be passed`); } else if (argTypeIsReference(argType)) { // TypeScript knows argType is ABIReferenceType here printInfo(` Reference type detected: ${argType}`); printInfo(` This arg will use foreign array indices`); } else { // TypeScript knows argType is ABIType here printInfo(` ABI type detected: ${argType.toString()}`); printInfo(` This arg will be ARC-4 encoded`); } }
printInfo('Testing type narrowing with "pay":'); demonstrateTypeNarrowing('pay' as ABIMethodArgType);
printInfo(''); printInfo('Testing type narrowing with "asset":'); demonstrateTypeNarrowing('asset' as ABIMethodArgType);
printInfo(''); printInfo('Testing type narrowing with ABIType.from("uint64"):'); demonstrateTypeNarrowing(ABIType.from('uint64'));
// Step 7: Practical example - Method argument handling printStep(7, 'Practical Example - Method Argument Handling');
printInfo('Consider a method: "swap(asset,asset,pay,uint64)uint64"'); printInfo('');
// Parse the method const swapMethod = ABIMethod.fromSignature('swap(asset,asset,pay,uint64)uint64');
printInfo(`Method name: ${swapMethod.name}`); printInfo(`Number of args: ${swapMethod.args.length}`); printInfo('');
// Analyze each argument swapMethod.args.forEach((arg, index) => { const argType = arg.type; let category: string; let handling: string;
if (argTypeIsTransaction(argType)) { category = 'Transaction'; handling = 'Pass a transaction object'; } else if (argTypeIsReference(argType)) { category = 'Reference'; handling = 'Will be added to foreign arrays, arg receives index'; } else { category = 'ABI'; handling = 'Will be ARC-4 encoded'; }
const typeStr = argTypeIsAbiType(argType) ? argType.toString() : argType; printInfo(` Arg ${index}: type="${typeStr}"`); printInfo(` Category: ${category}`); printInfo(` Handling: ${handling}`); printInfo(''); });
// Step 8: All type strings test matrix printStep(8, 'Complete Type String Test Matrix');
printInfo('Testing all type guard combinations:'); printInfo('');
const testTypes = [ 'txn', 'pay', 'keyreg', 'acfg', 'axfer', 'afrz', 'appl', 'account', 'asset', 'application', 'uint64', 'string', 'address', 'bool', 'AVMBytes', 'AVMString', 'AVMUint64', ];
printInfo(' Type | isTxn | isRef | isAbi | isAVM'); printInfo(' ----------------+-------+-------+-------+------');
for (const type of testTypes) { const isTxn = argTypeIsTransaction(type as ABIMethodArgType); const isRef = argTypeIsReference(type as ABIMethodArgType); const isAbi = argTypeIsAbiType(type as ABIMethodArgType); const isAvm = isAVMType(type);
const padType = type.padEnd(16); const padTxn = String(isTxn).padEnd(5); const padRef = String(isRef).padEnd(5); const padAbi = String(isAbi).padEnd(5);
printInfo(` ${padType}| ${padTxn} | ${padRef} | ${padAbi} | ${isAvm}`); }
// Step 9: Enum values demonstration printStep(9, 'Using ABITransactionType and ABIReferenceType Enums');
printInfo('The library provides enums for type safety:'); printInfo('');
printInfo('ABITransactionType enum:'); printInfo(` Txn: "${ABITransactionType.Txn}"`); printInfo(` Payment: "${ABITransactionType.Payment}"`); printInfo(` KeyRegistration: "${ABITransactionType.KeyRegistration}"`); printInfo(` AssetConfig: "${ABITransactionType.AssetConfig}"`); printInfo(` AssetTransfer: "${ABITransactionType.AssetTransfer}"`); printInfo(` AssetFreeze: "${ABITransactionType.AssetFreeze}"`); printInfo(` AppCall: "${ABITransactionType.AppCall}"`); printInfo('');
printInfo('ABIReferenceType enum:'); printInfo(` Account: "${ABIReferenceType.Account}"`); printInfo(` Asset: "${ABIReferenceType.Asset}"`); printInfo(` Application: "${ABIReferenceType.Application}"`);
// Step 10: Summary printStep(10, 'Summary');
printInfo('Type Guard Summary:'); printInfo(''); printInfo('Functions:'); printInfo( ' argTypeIsTransaction(type) - Returns true for txn, pay, keyreg, acfg, axfer, afrz, appl', ); printInfo(' argTypeIsReference(type) - Returns true for account, asset, application'); printInfo(' argTypeIsAbiType(type) - Returns true if NOT transaction AND NOT reference'); printInfo(' isAVMType(type) - Returns true for AVMBytes, AVMString, AVMUint64'); printInfo(''); printInfo('Use Cases:'); printInfo(' - Routing method arguments to appropriate handling logic'); printInfo(' - TypeScript type narrowing for safe property access'); printInfo(' - Determining encoding/decoding strategy based on type category'); printInfo(' - Validating method signatures and argument types'); printInfo(''); printInfo('Key Insight:'); printInfo(' The three arg type guards partition ABIMethodArgType:'); printInfo(' - Every ABIMethodArgType is exactly one of: Transaction, Reference, or ABI type'); printInfo(' - isAVMType is orthogonal - it checks for AVM-specific storage types');
printSuccess('Type Guards example completed successfully!');}
main();