Last active
March 7, 2024 19:54
-
-
Save nyteshade/03dcd7de33a2f3f87c2d989a76be527d to your computer and use it in GitHub Desktop.
CarrierString
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Creates a `CarrierString` by attaching additional properties to a string. | |
* This function allows for the creation of a string that carries extra data in a type-safe way. | |
* | |
* @param string The base string to which properties will be attached. | |
* @param key The key of the property to attach, or an object with multiple properties. | |
* @param value The value associated with the key, if `key` is not an object. | |
* @returns A new `CarrierString` with the attached properties. | |
* | |
* @example | |
* // Attaches a single key-value pair | |
* const result = CarrierString('text', 'id', 123); | |
* | |
* @example | |
* // Attaches multiple properties from an object | |
* const result = CarrierString('text', { id: 123, type: 'example' }); | |
*/ | |
export const CarrierString = (string, key, value) => { | |
const stringInstance = Object(string); | |
const objectKey = key; | |
let associatedValue = | |
key && typeof key === 'object' ? (key as AssociatedData<K, V>) : { [objectKey]: value }; | |
if (Object.getOwnPropertyNames(associatedValue).length > 1) { | |
associatedValue = { metadata: associatedValue }; | |
} | |
return Object.assign(stringInstance, associatedValue); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* A type representing either a string primitive or a String instance (i.e. new String(...) or | |
* Object('lorem ipsum dolor...')). **Do not remove or change type!** | |
*/ | |
// eslint-disable-next-line @typescript-eslint/ban-types | |
export type StringOrInstance = string | String; | |
/** | |
* Represents a key that can be used for object properties. It can be a string, a symbol, | |
* or a number. | |
* | |
* @example | |
* // Using a string as an object key | |
* const keyString: ObjectKey = 'name'; | |
* | |
* @example | |
* // Using a symbol as an object key | |
* const keySymbol: ObjectKey = Symbol('unique'); | |
* | |
* @example | |
* // Using a number as an object key | |
* const keyNumber: ObjectKey = 42; | |
*/ | |
export type ObjectKey = string | symbol | number; | |
/** | |
* Defines a mapping of keys to values, where keys are of type `ObjectKey` and values are of | |
* type `V`. This type is useful for creating objects with dynamic keys. | |
* | |
* @example | |
* // Defines an object with string, symbol, and number as keys, each with a value of type number. | |
* let data: AssociatedData<string | symbol | number, number> = { | |
* 'key1': 1, | |
* [Symbol('key2')]: 2, | |
* 3: 3 | |
* }; | |
*/ | |
export type AssociatedData<K extends ObjectKey, V> = { [key in K]: V }; | |
/** | |
* Represents a string with optional additional properties. | |
* | |
* This type extends a string instance with a set of optional keys, where each key is of type | |
* `K` and associated with a value of type `V`. It is used to create string objects that can | |
* carry additional data in a type-safe manner. | |
* | |
* @example | |
* // Usage with a string and an additional numeric property | |
* let carrier: CarrierString<'id', number> = Object.assign('example', { id: 123 }); | |
*/ | |
export type CarrierString<K extends ObjectKey, V> = StringOrInstance & AssociatedData<K, V>; | |
/** | |
* Creates a `CarrierString` by attaching additional properties to a string. | |
* This function allows for the creation of a string that carries extra data in a type-safe way. | |
* | |
* @param string The base string to which properties will be attached. | |
* @param key The key of the property to attach, or an object with multiple properties. | |
* @param value The value associated with the key, if `key` is not an object. | |
* @returns A new `CarrierString` with the attached properties. | |
* @throws a TypeError if the key is an object with more than one property; sending an object | |
* as the key with a single property and any type of value is supported, but if you need more | |
* than one key value pair for associated data, supply a valid {@link ObjectKey} for the key | |
* and a complex value for the value. | |
* | |
* @example | |
* // Attaches a single key-value pair | |
* const result = CarrierString('text', 'id', 123); | |
* | |
* @example | |
* // Attaches multiple properties from an object | |
* const result = CarrierString('text', 'meta', { id: 123, type: 'example' }); | |
* | |
* @example | |
* // This will throw a TypeError | |
* const result = CarrierString('nope', {id: 123, type: 'example'}); | |
*/ | |
export const CarrierString = <K extends ObjectKey, V>( | |
string: string, | |
key: K | AssociatedData<K, V>, | |
value?: V, | |
): CarrierString<K, V> => { | |
const stringInstance: CarrierString<K, V> = Object(string); | |
const objectKey = key as ObjectKey; | |
const associatedValue = | |
key && typeof key === 'object' ? (key as AssociatedData<K, V>) : { [objectKey]: value }; | |
if (Object.getOwnPropertyNames(associatedValue).length > 1) { | |
const assocValueStr = inspect(associatedValue, { colors: true }).replaceAll(/\n/g, ' '); | |
throw new TypeError( | |
[ | |
'An object with more than one key was supplied as the key and value. The', | |
`supplied value was ${assocValueStr}. Either supply a separate key and the`, | |
'complex value as an object or alter your approach', | |
].join(' '), | |
); | |
} | |
return Object.assign(stringInstance, associatedValue); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment