Skip to content
Algorand Developer Portal

Type Guards

← Back to ABI Encoding

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
  • No LocalNet required

From the repository root:

Terminal window
cd examples
npm run example abi/13-type-guards.ts

View source on GitHub

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