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:
key
(string
): The key for which the getter and setter functions are created.
Returns a function that:
- Accepts an adaptable object of the type
A
. - Returns a tuple:
- Getter (
get() => T
): Retrieves the value associated with the key from the stack, orundefined
if not found. - Setter (
set(value: T) => T
): Sets the value in the top context object of the stack.
- Getter (
Behavior Details
- The
get
function assumes that the adaptable object can be indexed by string keys and returns the value stored at the specifiedadapterKey
. - The
set
function: - Assigns the value to the specified key on the object.
- If the value is
undefined
, it deletes the property from the object instead. - Returns the value that was passed in.
Notes
- This method is fully generic and can be used with any object-like structure that can be indexed with strings.
- It’s particularly useful for encapsulating access patterns in larger systems where multiple modules might interact with the same object structure.