Extending TypeScript interface definitions for Airtable automation environment

I’m setting up a TypeScript development environment for Airtable automations and using rollup to include external libraries. The @airtable/blocks package provides type definitions that are closest to the actual Airtable automation runtime.

However, I’m encountering an issue where table.getRecordAsync method doesn’t exist in the @airtable/blocks types, and my module augmentation approach isn’t working properly.

// automation.ts
import { helperFunction } from './utilities';
const dataTable = base.getTable('UserData');

// TypeScript error occurs here at .getRecordAsync
const userRecord = await dataTable.getRecordAsync('rec456', { fields: ['Email'] });
helperFunction(userRecord.getCellValueAsString('Email'));
export {};
// declarations.d.ts
import { type RecordQueryResultOpts } from '@airtable/blocks/dist/types/src/models/record_query_result';
import type TableOrViewQueryResult from '@airtable/blocks/dist/types/src/models/table_or_view_query_result';

declare module '@airtable/blocks' {
  interface Table {
    getRecordAsync(recordId: string, options?: RecordQueryResultOpts): Promise<TableOrViewQueryResult>;
  }
}

declare global {
  const base: typeof import('@airtable/blocks').base;
  const input: {
    config(): any;
  };
  const output: {
    set(key: string, value: any): void;
  };
}

The error I’m getting:

automation.ts → build...
(!) [plugin typescript] automation.ts (4:35): @rollup/plugin-typescript TS2551: Property 
'getRecordAsync' does not exist on type 'Table'. 
Did you mean 'selectRecordsAsync'? automation.ts:4:35
4 const userRecord = await dataTable.getRecordAsync('rec456', { fields: ['Email'] });

Is there a way to properly extend these type definitions without modifying the original @airtable/blocks source files?

The problem is rollup processes files before TypeScript can merge your module augmentation. I’ve hit this exact issue with automation projects.

Your declarations.d.ts looks right, but rollup ignores it during compilation. Here’s what actually works:

Make a types.ts file (not .d.ts) that exports your extended types:

// types.ts
import { Table } from '@airtable/blocks';

export interface ExtendedTable extends Table {
  getRecordAsync(recordId: string, options?: any): Promise<any>;
}

Then import and use it:

// automation.ts
import type { ExtendedTable } from './types';
const dataTable = base.getTable('UserData') as ExtendedTable;
const userRecord = await dataTable.getRecordAsync('rec456', { fields: ['Email'] });

This forces rollup to handle the type extension as a regular import instead of ambient module augmentation. Using a .ts file that gets bundled beats a .d.ts file that gets processed separately.

I learned this after wasting days on module augmentation that worked in dev but broke during rollup builds. The explicit import approach works reliably across different bundler setups.

Yeah, rollup definitely screws with TypeScript compilation order. Skip the module augmentation headaches - just wrap it in a simple function:

const getRecord = (table: any, id: string, opts?: any) => table.getRecordAsync(id, opts);

const userRecord = await getRecord(dataTable, 'rec456', { fields: ['Email'] });

TypeScript treats it like any normal function call and you’re not wrestling with the type system.

I hit the same thing with custom APIs that had sketchy type definitions. Your module augmentation isn’t getting picked up by TypeScript during the rollup build.

First, double-check that getRecordAsync actually exists in the Airtable automation runtime. Sometimes docs lie. Test it with a console.log to see what methods are available:

console.log(Object.getOwnPropertyNames(Object.getPrototypeOf(dataTable)));

If the method’s there, skip module augmentation and try this instead:

// types/airtable-extensions.d.ts
interface ExtendedTable {
  getRecordAsync(recordId: string, options?: any): Promise<any>;
}

// automation.ts
const dataTable = base.getTable('UserData') as any as ExtendedTable;
const userRecord = await dataTable.getRecordAsync('rec456', { fields: ['Email'] });

Make sure your tsconfig.json includes the types directory and set "moduleResolution": "node". Also add "skipLibCheck": true to avoid conflicts with the original package types.

This casting approach saved me when module augmentation failed with rollup.

Had the same nightmare with Airtable automation types recently. The rollup processes your TypeScript before module augmentation gets merged properly. Instead of fighting module augmentation, I used a hybrid approach with ambient globals that actually works. Create airtable-globals.d.ts and declare everything at global scope:

declare global {
  interface AirtableTable {
    getRecordAsync(recordId: string, options?: any): Promise<any>;
    selectRecordsAsync(): any;
  }
  const base: {
    getTable(name: string): AirtableTable;
  };
}
export {};

Then cast the table to your custom interface in your automation file:

const dataTable = base.getTable('UserData') as AirtableTable;
const userRecord = await dataTable.getRecordAsync('rec456', { fields: ['Email'] });

Just make sure this declarations file gets included in your tsconfig.json and processed before your main automation code. This worked consistently across different bundlers where module augmentation kept breaking.

Honestly, TypeScript module augmentation and type juggling is exactly why I stopped trying to patch existing type definitions. Rollup makes it worse since it processes things in random orders.

I ran into the same issues building cross-platform automations. Instead of fighting type definitions, I built a simple automation workflow that handles Airtable operations through API calls.

Set up a workflow that takes your record ID and field requirements as input, makes the Airtable API calls, and returns formatted data your TypeScript code expects. No more type headaches.

I use this approach for all my data integration now. The workflow handles Airtable specifics while your TypeScript code stays clean and properly typed. Plus you get better error handling and can easily add retry logic or data transformation.

Way cleaner than casting types or fighting module augmentation that breaks every dependency update.

Module augmentation gets messy with rollup builds. Your declarations file probably isn’t being processed in the right order during compilation. I’ve hit this same TypeScript module augmentation issue before. Skip extending the existing interface and create a custom ambient declaration file instead. Make a new file called airtable-runtime.d.ts in your project root:

declare namespace AirtableRuntime {
  interface Table {
    getRecordAsync(recordId: string, options?: { fields?: string[] }): Promise<any>;
    selectRecordsAsync(): any;
  }
}

declare const base: {
  getTable(name: string): AirtableRuntime.Table;
};

In your tsconfig.json, add "typeRoots": ["./", "./node_modules/@types"] and include your declarations file in the "include" array.

This bypasses module augmentation completely and creates a parallel type system that matches the actual runtime behavior. Not as elegant but way more reliable with bundlers like rollup.

try moving your declarations.d.ts to the root folder and include it in your tsconfig.json. Also, double-check the return type for getRecordAsync; it might be returning a Record instead of TableOrViewQueryResult. restart your TS server after you’ve updated things.