newAdapter

The newAdapter function allows to define adapters for an object or a function. It is useful to extend existing functionality without adding new methods to the extended entity.

This method creates a reusable getter/setter methods pair for manipulating a property of an object using a specified key.

Implementation

Implementation of the newAdapter pattern can vary. In the simplest case adapters are attached to the object itself using a string key. Other implementations could use Map or WeakMap instances to bind functionalities to adaptable objects.

Key-based Implementation
export function newAdapter<T, A = unknown>(
  adapterKey: string,
): [get: (adaptable: A) => T, set: (adaptable: A, value: T) => T] {
  return [
    function get(adaptable: A) {
      return (adaptable as Record<string, T>)[adapterKey];
    },
    function set(adaptable: A, value: T) {
      if (value === undefined) {
        delete (adaptable as Record<string, T>)[adapterKey];
      } else {
        (adaptable as Record<string, T>)[adapterKey] = value;
      }
      return value;
    },
  ];
}
Map-based Implementation

Note: This implementation do not use keys.

export function newAdapter<T, A = unknown>(): [
  get: (adaptable: A) => T,
  set: (adaptable: A, value: T) => T,
] {
  // It could be also WeakMap<A,T> for objects
  const index = new Map<A, T>(); 
  return [
    function get(adaptable: A) {
      return index.get(adaptable) as T;
    },
    function set(adaptable: A, value: T) {
      if (value === undefined) {
        index.delete(adaptable);
      } else {
        index.set(adaptable, value);
      }
      return value;
    },
  ];
}

Examples

newAdapter method examples:

const object = {};
const [get, set] = newAdapter<number | undefined>("counter");

console.log(get(object)); // Outputs: undefined
set(object, 42);
console.log(get(object)); // Outputs: 42

Access to an object with an existing property:

const [getName, setName] = newAdapter<string, { name?: string }>('name');

const user = { name: 'Alice' };
console.log(getName(user)); // "Alice"

setName(user, 'Bob');
console.log(user.name); // "Bob"

An advanced example:

type Settings = { darkMode?: boolean; volume?: number };

const [getVolume, setVolume] = newAdapter<number, Settings>('volume');
const [getDarkMode, setDarkMode] = newAdapter<boolean, Settings>('darkMode');

const config: Settings = {};

setVolume(config, 75);
setDarkMode(config, true);

console.log(getVolume(config));    // 75
console.log(getDarkMode(config));  // true

setVolume(config, undefined);
console.log(config);               // { darkMode: true }

Method Signature

function newAdapter<T, A = unknown>(
  key: string
) => [
  get: (adaptable: A) => T | undefined,
  set: (adaptable: A, value: T) => T
];

Parameters:

Returns a function that:

Behavior Details

Notes