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()