Created
November 27, 2024 17:37
-
-
Save sirlancelot/8310b07fdc01136945e622a9bef6a360 to your computer and use it in GitHub Desktop.
A generic way to overload a return value for supporting multiple, optional values.
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
/** | |
* Use this to return a non-primitive value which can be assigned to a single | |
* variable or destructured to assign multiple variables. It is strongly typed | |
* for ease of use. | |
* | |
* Usage: | |
* | |
* ```ts | |
* function somethingReturningMultipleValues() { | |
* const primaryResult = { id: 1, hello: "world" } | |
* | |
* return withOptionalValues(primaryResult, ["def", "ghi"] as const) | |
* } | |
* ``` | |
* | |
* If your additional values are expensive to compute, you can use a factory | |
* function to generate them: | |
* | |
* ```ts | |
* function somethingReturningMultipleValues() { | |
* const primaryResult = { id: 1, hello: "world" } | |
* | |
* return withOptionalValues(primaryResult, () => ["def", "ghi"] as const) | |
* } | |
* ``` | |
* | |
* This was inspired by Vue.js `defineModel()` function, which supports this | |
* same pattern in a more specific way: https://vuejs.org/guide/components/v-model.html | |
*/ | |
/** | |
* A value with a `primary` value, which can also be destructured to assign | |
* optional values. | |
*/ | |
export type WithOptionalValues<T extends object, U extends [...any]> = T & | |
[primary: T, ...more: U, ...bad: unknown[]] | |
/** | |
* Return a value with optional additional values accessible via destructuring. | |
* | |
* Overload 1: Factory function of additional values. For use when the | |
* additional values require extra computation. | |
*/ | |
export function withOptionalValues<T extends object, U extends [...any]>( | |
primary: T, | |
factory: () => readonly [...U] | |
): WithOptionalValues<T, U> | |
/** | |
* Return a value with optional additional values accessible via destructuring. | |
* | |
* Overload 2: Static Array of additional values. For use when the values are | |
* cheap to compute. | |
*/ | |
export function withOptionalValues<T extends object, U extends [...any]>( | |
primary: T, | |
more: readonly [...U] | |
): WithOptionalValues<T, U> | |
/** Implementation */ | |
export function withOptionalValues<T extends object, U extends [...any]>( | |
primary: T, | |
maybeFactory: U | (() => U) | |
): WithOptionalValues<T, U> { | |
// @ts-expect-error | |
primary[Symbol.iterator] = function* () { | |
yield primary | |
yield* typeof maybeFactory === "function" | |
? maybeFactory() | |
: maybeFactory | |
} | |
return primary as any | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Scenario, a memoization function which can optionally have its memory cleared. The return type function signature mirrors the passed in function exactly, but a consumer may wish to opt-in to enhanced behavior.