{"id":26255421,"url":"https://github.com/doeixd/named-arguments","last_synced_at":"2025-07-14T16:04:14.946Z","repository":{"id":279673117,"uuid":"852981126","full_name":"doeixd/named-arguments","owner":"doeixd","description":"Pseudo named arguments for JavaScript functions","archived":false,"fork":false,"pushed_at":"2025-05-20T15:12:03.000Z","size":370,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-31T21:50:52.631Z","etag":null,"topics":["currying","currying-function-javascript","named-arguments","partial-application","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/doeixd.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-09-05T19:07:26.000Z","updated_at":"2025-05-20T15:12:06.000Z","dependencies_parsed_at":"2025-02-26T20:11:18.033Z","dependency_job_id":"42a399f3-03cf-46a2-a92c-4a876b4c9fc7","html_url":"https://github.com/doeixd/named-arguments","commit_stats":null,"previous_names":["doeixd/named-arguments"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/doeixd/named-arguments","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doeixd%2Fnamed-arguments","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doeixd%2Fnamed-arguments/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doeixd%2Fnamed-arguments/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doeixd%2Fnamed-arguments/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/doeixd","download_url":"https://codeload.github.com/doeixd/named-arguments/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doeixd%2Fnamed-arguments/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263013703,"owners_count":23399812,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["currying","currying-function-javascript","named-arguments","partial-application","typescript"],"created_at":"2025-03-13T19:18:42.984Z","updated_at":"2025-07-14T16:04:14.930Z","avatar_url":"https://github.com/doeixd.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🏷️ Named Arguments\n\n[![NPM Version][npm-image]][npm-url]\n[![License][license-image]][license-url]\n[![Downloads][downloads-image]][downloads-url]\n\nThis library brings **type-safe named arguments and flexible partial application** to your functions, improving code readability, maintainability, and developer experience.\n\n## ✨ Why Use This Library?\n\n-   **Clarity \u0026 Readability:** Call functions with arguments by name (`args.userId(...)`) instead of relying on order. Code becomes self-documenting.\n-   **Type Safety:** Catch errors at compile time for missing required arguments, incorrect types, or accidentally reapplying the same argument during partial application.\n-   **Refactor with Confidence:** Changing the order of parameters in your original function doesn't break existing calls using named arguments.\n-   **Flexible Partial Application:** Apply arguments in any order, incrementally building specialized functions without the constraints of traditional currying.\n-   **Simplified Configuration:** Handle functions with many parameters (especially optional ones) more elegantly than large option objects.\n-   **Enhanced Composability:** Utilities for building, configuring, and mapping arguments enable powerful patterns.\n\n## 📦 Installation\n\n```bash\nnpm install @doeixd/named-args\n# or\nyarn add @doeixd/named-args\n# or\npnpm add @doeixd/named-args\n# or\nbun install @doeixd/named-args\n```\n\n## 🚀 Quick Example\n```ts\nimport { createNamedArguments } from '@doeixd/named-args';\n\n// A function with several parameters\nfunction createUser(firstName: string, lastName: string, age: number, email: string) {\n  return { firstName, lastName, age, email };\n}\n\n// Create named arguments for the function\n// The type parameter specifies the argument names matching the function parameters\nconst [args, namedCreateUser] = createNamedArguments\u003c\n  typeof createUser,\n  {firstName: string, lastName: string, age: number, email: string}\n\u003e(createUser);\n\n// Use named arguments in any order\nconst user = namedCreateUser(\n  args.email('john.doe@example.com'),\n  args.firstName('John'),\n  args.age(30),\n  args.lastName('Doe')\n);\n\nconsole.log(user);\n// { firstName: 'John', lastName: 'Doe', age: 30, email: 'john.doe@example.com' }\n```\n\n\n## 📑 Table of Contents\n\n-   [Installation](#-installation)\n-   [Quick Example](#-quick-example)\n-   [Core Concepts](#-core-concepts)\n    -   [Argument Branding](#argument-branding)\n    -   [Function Transformation](#function-transformation)\n-   [Core Usage (`createNamedArguments`)](#core-usage-createnamedarguments)\n    -   [Partial Application](#-partial-application)\n    -   [Updating Object Parameters (`reApply`)](#updating-object-parameters-reapply)\n-   [Handling Nested Objects](#handling-nested-objects)\n    -   [First-Level Access (Built-in)](#first-level-access-built-in)\n    -   [Deep Access (`createNestedArgs`)](#deep-access-createnestedargs)\n    -   [Property-Level Access (`createObjectPropertyArgs`)](#property-level-access-createobjectpropertyargs)\n-   [Advanced Patterns](#advanced-patterns)\n    -   [Builder Pattern (`createBuilder`)](#-builder-pattern)\n    -   [Configurable Functions (`createConfigurableFunction`)](#-configurable-functions)\n    -   [Mapped Arguments (`createMappedNamedArguments`)](#mapped-arguments-createmappednamedarguments)\n-   [Advanced Features](#-advanced-features)\n    -   [Rest Parameters](#rest-parameters)\n    -   [Default Values](#default-values)\n    -   [Parameter Metadata](#parameter-metadata)\n-   [Use Cases \u0026 Examples](#use-cases--examples)\n-   [Comparison with Options Objects](#comparison-with-options-objects)\n-   [Gotchas \u0026 Troubleshooting](#gotchas--troubleshooting)\n-   [API Reference](#-api-reference)\n-   [Compatibility](#compatibility)\n-   [Contributing](#contributing)\n-   [License](#-license)\n\n## 🧩 Core Concepts\n\n### Argument Branding\n\nEach argument created via the `args` object (e.g., `args.name('Alice')`) is internally \"branded\" using a `Symbol`. This brand associates the provided value (`'Alice'`) with the intended parameter name (`'name'`). This allows the wrapped function to correctly place arguments regardless of their call order.\n\n### Function Transformation\n\n`createNamedArguments` takes your original function `F` and returns:\n1.  `args`: An object with typed methods (`NamedArg` or `CallableObject`) for creating branded arguments (e.g., `args.name: (v: string) =\u003e BrandedArg\u003cstring, 'name'\u003e`). The structure of `args` mirrors the type `A` you provide.\n2.  `func`: A `BrandedFunction\u003cF\u003e` wrapper around your original function. This wrapper accepts the branded arguments, reconstructs the correct positional argument list based on brands and parameter info, and then calls your original function `F`. It also provides methods like `.partial` and `.reApply`.\n\n## Core Usage (`createNamedArguments`)\n\nThis is the primary way to enable named arguments for your functions.\n\n```typescript\nimport { createNamedArguments } from '@doeixd/named-args';\n\nfunction format(value: number, prefix = '$', precision = 2): string {\n    return `${prefix}${value.toFixed(precision)}`;\n}\n// Define the argument structure type\ntype FormatArgs = { value: number; prefix?: string; precision?: number };\n\n// Create args and the named function wrapper\nconst [args, namedFormat] = createNamedArguments\u003ctypeof format, FormatArgs\u003e(\n  format,\n  // Optional but recommended: ParameterInfo for accurate required/optional/default handling\n  [\n    { name: 'value', required: true },\n    { name: 'prefix', required: false, defaultValue: '$' },\n    { name: 'precision', required: false, defaultValue: 2 }\n  ]\n);\n\n// Call the wrapper function with branded arguments\nconst formatted = namedFormat(args.value(123.456), args.precision(1)); // \"$123.5\"\nconst defaultFormatted = namedFormat(args.value(99)); // \"$99.00\" (uses defaults)\n```\n\n### 🧪 Partial Application\n\nThe `BrandedFunction` returned by `createNamedArguments` supports type-safe partial application.\n\n#### Precise Return Types\n\nBased on whether all *required* parameters (as defined by `ParameterInfo`) have been supplied, TypeScript correctly infers if the call returns the final function result or another `BrandedFunction` waiting for more arguments.\n\n```typescript\n// Returns string: 'value' is required and provided.\nconst result1: string = namedFormat(args.value(10));\n\n// Returns function: Required 'value' is missing.\nconst partial1: BrandedFunction\u003ctypeof format, ['prefix']\u003e = namedFormat.partial(\n    args.prefix('USD ')\n);\n\n// Complete later\nconst result2: string = partial1(args.value(50)); // \"USD 50.00\"\n```\n\n#### Type-Safe Application\n\nThe type system prevents applying the same **base parameter** multiple times across separate `.partial()` calls or direct partial calls.\n\n```typescript\nfunction add(a: number, b: number, c: number): number { return a + b + c; }\ntype AddArgs = { a: number; b: number; c: number };\nconst [args, namedAdd] = createNamedArguments\u003ctypeof add, AddArgs\u003e(add);\n\nconst addWithA = namedAdd.partial(args.a(5));\n\n// Compile-time Errors below! Parameter \"a\" was already applied.\n// const error1 = addWithA(args.a(10));\n// const error2 = addWithA.partial(args.a(10));\n\nconst addWithAB = addWithA.partial(args.b(10)); // OK\nconst result = addWithAB(args.c(15)); // OK -\u003e 30\n```\n*(See [Partial Application with Objects](#partial-application-with-objects) in Gotchas for limitations when partially applying properties of the same object parameter.)*\n\n#### Multi-Stage Partial Application\n\nBuild specialized functions incrementally:\n\n```typescript\n// Assume makeRequest function and RequestArgs type exist\nconst [reqArgs, namedRequest] = createNamedArguments\u003ctypeof makeRequest, RequestArgs\u003e(makeRequest);\n\n// Stage 1: Base API config\nconst apiRequest = namedRequest.partial(reqArgs.headers({ /* ... */ }));\n// Stage 2: Method-specific\nconst getRequest = apiRequest.partial(reqArgs.method('GET'));\n// Stage 3: Domain-specific\nconst userApiGet = getRequest.partial(reqArgs.url('...'), reqArgs.timeout(5000));\n// Stage 4: Final call\nconst userData = userApiGet(reqArgs.query({ active: true })); // Execute\n```\n\n### Updating Object Parameters (`reApply`)\n\nSafely update parts of an *already applied* object parameter without re-specifying the whole object, using an updater function.\n\n```typescript\n// Assume setup function and SetupArgs type exist\nconst [args, namedConfigure] = createNamedArguments\u003ctypeof setup, SetupArgs\u003e(setup);\n\nconst baseConfig = namedConfigure.partial(\n  args.port(80),\n  args.options({ poolSize: 10, retry: { attempts: 3, delay: 100 } })\n);\n\n// Use reApply to update options based on the previous value\nconst updatedConfig = baseConfig.reApply(\n  args.options, // Specify which parameter's arg creator to use\n  (prevOpts) =\u003e ({ // Updater function receives previous value\n    ...prevOpts,\n    retry: { ...prevOpts.retry, attempts: 5 } // Return the updated object\n  })\n);\n\nupdatedConfig(args.host('server.com')).execute(); // Uses options with attempts: 5\n```\n\n## Handling Nested Objects\n\nThe library provides ways to work with object parameters containing nested properties.\n\n### First-Level Access (Built-in)\n\nBy default, `createNamedArguments` generates `CallableObject` accessors for object parameters. These allow both setting the entire object *and* accessing its first-level properties directly.\n\n```typescript\nfunction setOptions(opts: { timeout: number; retries?: number }) { /* ... */ }\ntype OptionArgs = { opts: { timeout: number; retries?: number } };\n\nconst [args, namedSetOptions] = createNamedArguments\u003ctypeof setOptions, OptionArgs\u003e(setOptions);\n\n// Set the whole object:\nnamedSetOptions(args.opts({ timeout: 5000, retries: 3 }));\n\n// Set first-level properties individually (brands as \"opts.timeout\", \"opts.retries\"):\nnamedSetOptions(args.opts.timeout(3000), args.opts.retries(1));\n```\n\n### Deep Access (`createNestedArgs`)\n\nFor accessing properties nested deeper than the first level with type safety, use the `createNestedArgs` utility.\n\n```typescript\nimport { createNamedArguments, createNestedArgs } from '@doeixd/named-args';\n\nfunction setupApp(config: { server: { port: number; ssl: { enabled: boolean } } }) { /* ... */ }\ntype SetupAppArgs = { config: { server: { port: number; ssl: { enabled: boolean } } } };\ntype ConfigType = SetupAppArgs['config']; // Get the specific type\n\nconst [args, namedSetup] = createNamedArguments\u003ctypeof setupApp, SetupAppArgs\u003e(setupApp);\n\n// Create nested args proxy for the 'config' parameter\nconst configArgs = createNestedArgs\u003cConfigType\u003e('config');\n\n// Use intuitive dot notation\nnamedSetup(\n  configArgs.server.port(8080),          // Brands as \"config.server.port\"\n  configArgs.server.ssl.enabled(true)  // Brands as \"config.server.ssl.enabled\"\n);\n```\n\n### Property-Level Access (`createObjectPropertyArgs`)\n\nIf you only need individual top-level property accessors without the ability to set the whole object via the same accessor, `createObjectPropertyArgs` is a simpler utility.\n\n```typescript\nimport { createNamedArguments, createObjectPropertyArgs } from '@doeixd/named-args';\n\nfunction configureServer(options: { port: number; host: string; }) { /* ... */ }\ntype ServerOptsArgs = { options: { port: number; host: string; } };\ntype OptionsType = ServerOptsArgs['options'];\n\nconst [args, namedConfig] = createNamedArguments\u003ctypeof configureServer, ServerOptsArgs\u003e(configureServer);\n\n// Create individual property accessors for 'options'\nconst optionArgs = createObjectPropertyArgs\u003cOptionsType\u003e('options');\n\n// Use them (cannot call optionArgs(...) itself)\nnamedConfig(\n  optionArgs.port(9000),   // Brands as \"options.port\"\n  optionArgs.host('local') // Brands as \"options.host\"\n);\n```\n\n## Advanced Patterns\n\n### 🏗️ Builder Pattern (`createBuilder`)\n\nProvides a fluent interface (`.with(...).execute()`) for accumulating arguments before execution, with type safety preventing duplicate parameter application within the builder chain.\n\n```typescript\nimport { createNamedArguments, createBuilder } from \"@doeixd/named-args\";\n\nfunction configureApp(port: number, host: string, db: DbConfig, logging?: boolean) { /* ... */ }\ntype AppArgs = { port: number; host: string; db: DbConfig; logging?: boolean };\ninterface DbConfig { url: string; name: string };\n\nconst [args, namedConfig] = createNamedArguments\u003ctypeof configureApp, AppArgs\u003e(configureApp);\n\nconst appBuilder = createBuilder(namedConfig);\n\nconst devConfig = appBuilder\n  .with(args.port(3000))\n  .with(args.host(\"localhost\"))\n  .with(args.db({ url: \"localhost:27017\", name: \"devdb\" }))\n  // .with(args.port(3100)) // Compile-time error!\n  .execute();\n\nconsole.log(devConfig);\n```\n\n### ⚙️ Configurable Functions (`createConfigurableFunction`)\n\nCreates higher-order functions for pre-configuring named argument functions. Useful for dependency injection or creating specialized function variants.\n\n```typescript\nimport { createNamedArguments, createConfigurableFunction } from '@doeixd/named-args';\n\nfunction processArray\u003cT\u003e(arr: T[], filterFn: (i: T) =\u003e boolean, sortFn?: (a: T, b: T) =\u003e number) { /* ... */ }\ntype ProcessArgs\u003cTItem\u003e = { arr: TItem[]; filterFn: (i: TItem) =\u003e boolean; sortFn?: (a: TItem, b: TItem) =\u003e number };\n\nconst [args, namedProcess] = createNamedArguments\u003ctypeof processArray, ProcessArgs\u003cnumber\u003e\u003e(processArray);\n\nconst configureProcessor = createConfigurableFunction([args, namedProcess]);\n\n// Define a configuration for positive numbers, descending\nconst topPositiveProcessor = configureProcessor(cfgArgs =\u003e {\n  cfgArgs.filterFn(num =\u003e num \u003e 0);\n  cfgArgs.sortFn((a, b) =\u003e b - a);\n});\n\n// Use the configured processor with the remaining 'arr' argument\nconst numbers = [-5, 10, 3, -2, 8, 1];\nconst result = topPositiveProcessor(args.arr(numbers)); // [10, 8, 3, 1]\n```\n\n### Mapped Arguments (`createMappedNamedArguments`)\n\n*(Located in `@doeixd/named-args/mapped`)*\n\nThis advanced utility creates a custom `args` interface based on an explicit mapping you define. It allows renaming arguments and mapping directly to nested properties. Crucially, its returned wrapper function (`MappedBrandedFunction`) provides **partial application based on the mapped keys**, differing from the core library's base-parameter tracking.\n\n**Use Cases:**\n-   Creating a public API for a function with different argument names than the internal implementation.\n-   Enabling incremental partial application of properties targeting the same underlying object parameter (e.g., `.partial(args.hostName(...))` followed by `.partial(args.portNumber(...))`).\n\n**Requires:**\n-   Explicitly providing the base argument structure type `A`.\n-   Defining the mapping configuration object using `as const`.\n\n```typescript\nimport { createMappedNamedArguments } from '@doeixd/named-args/mapped'; // Adjust import\n\nfunction complexTarget(id: string, config: { host: string; port: number; user?: { email: string } }) { /* ... */ }\ntype ComplexTargetArgs = { id: string; config: { host: string; port: number; user?: { email: string } } };\n\n// Define the explicit mapping using 'as const'\nconst argMapSpec = {\n  serverId: 'id',                 // Rename 'id' -\u003e 'serverId'\n  hostname: 'config.host',        // Map 'hostname' -\u003e 'config.host'\n  portNumber: 'config.port',      // Map 'portNumber' -\u003e 'config.port'\n  notifyEmail: 'config.user.email' // Map 'notifyEmail' -\u003e 'config.user.email'\n} as const; // \u003c-- Use 'as const'\n\n// Create mapped args and the custom function wrapper\nconst [args, mappedFunc] = createMappedNamedArguments\u003c\n  typeof complexTarget, // F\n  ComplexTargetArgs,    // A (still required)\n  typeof argMapSpec     // Spec (inferred from const object)\n\u003e(argMapSpec, complexTarget);\n\n// Partial application based on mapped keys works incrementally\nconst partial1 = mappedFunc.partial(args.hostname('server.com'));\nconst partial2 = partial1.partial(args.portNumber(443)); // OK: portNumber and hostname are distinct mapped keys\n// const partial3 = partial2.partial(args.hostname('new.com')); // COMPILE ERROR: hostname already applied\n\nconst result = partial2(args.serverId('srv-1'), args.notifyEmail('a@b.c')).execute();\n```\n\n\n## 🚀 Advanced Features\n\n### Rest Parameters\n\nRest parameters (`...name: type[]`) are supported via the corresponding `args` accessor.\n\n```typescript\nfunction sum(label: string, ...numbers: number[]) { /* ... */ }\ntype SumArgs = { label: string; numbers: number[] };\nconst [args, namedSum] = createNamedArguments\u003ctypeof sum, SumArgs\u003e(sum);\n\n// Pass individual values or an array\nnamedSum(args.label('Nums:'), args.numbers(1, 5, 9));\nnamedSum(args.label('Nums:'), args.numbers([1, 5, 9]));\n```\n\n### Default Values\n\nJavaScript/TypeScript default parameter values (`param = defaultValue`) are respected by the runtime if an argument isn't provided via `args`.\n\n```typescript\nfunction greet(name: string, greeting = \"Hello\") { /* ... */ }\ntype GreetArgs = { name: string; greeting?: string };\nconst [args, namedGreet] = createNamedArguments\u003ctypeof greet, GreetArgs\u003e(greet);\n\nnamedGreet(args.name(\"Galaxy\")); // Output uses \"Hello\"\n```\n\n### Parameter Metadata\n\nProvide an explicit `ParameterInfo[]` array to `createNamedArguments` or `createMappedNamedArguments` for precise control over `required` status and `defaultValue` handling, improving type safety and runtime checks. Inference via `toString()` is less reliable.\n\n```typescript\nconst [args, func] = createNamedArguments(myFunc, [\n  { name: 'id', required: true },\n  { name: 'timeout', required: false, defaultValue: 5000 }\n]);\n```\n\n## Use Cases \u0026 Examples\n\n\n\n-   **Type-Safe Function Composition:** Chain partially applied functions safely.\n-   **Dependency Injection:** Pre-configure services with dependencies.\n-   **Example: Building a Chart API:** Make complex configurations type-safe and readable.\n\n```typescript\n// Chart API Example (Simplified)\nimport { createNamedArguments } from '@doeixd/named-args';\n\n// Define types (replace with actual library types)\ntype ChartElement = HTMLElement; type ChartData = any[];\ninterface AxisOptions { /* ... */ } interface ChartOptions { /* ... */ }\nfunction renderChart(element: ChartElement, data: ChartData[], options: ChartOptions) { /* ... */ }\ntype ChartArgs = { element: ChartElement; data: ChartData[]; options: ChartOptions };\n\nconst [args, namedRenderChart] = createNamedArguments\u003ctypeof renderChart, ChartArgs\u003e(renderChart);\n\n// Type-safe call using named args\nconst myChart = namedRenderChart(\n  args.element(document.getElementById('chart')!),\n  args.data(myData),\n  args.options({ type: 'line', xAxis: { /*...*/ } })\n);\n// Use first-level accessor\nconst myChart2 = namedRenderChart(\n  args.element(document.getElementById('chart2')!),\n  args.data(myData),\n  args.options.type('bar') // Set type directly\n);\n```\n\n## Comparison with Options Objects\n\n| Feature               | Named Arguments (`createNamedArguments`) | Options Object Pattern (`fn(opts)`)       |\n| :-------------------- | :--------------------------------------- | :---------------------------------------- |\n| **Readability**       | High (Self-documenting calls)            | Medium (Depends on option names)          |\n| **Parameter Order**   | Independent                              | Single object, internal order matters     |\n| **Type Safety**       | High (Compile-time checks for args/types) | Medium (Relies on options type definition) |\n| **Optional Args**     | Clearly handled                          | Managed within the options object type    |\n| **Refactoring**       | Safer (Order doesn't matter)             | Safer (Internal changes less impact)      |\n| **Partial Application** | Built-in, Type-Safe                     | Manual implementation required            |\n| **Discoverability**   | High (IDE Autocomplete on `args`)        | Medium (Depends on options type export)   |\n| **Runtime Overhead**  | Small                                    | Minimal                                   |\n| **Boilerplate**       | Some (Type `A`, `create...` call)        | Some (Interface/Type for options)         |\n\nNamed arguments excel when functions have multiple parameters (especially optional ones) or when partial application and composability are desired. Options objects are simpler for functions with a single configuration bundle.\n\n## 💡 Why This Matters\n\n-   Reduces errors caused by incorrect argument order or type mismatches.\n-   Improves code clarity and makes function calls easier to understand.\n-   Enhances refactoring safety.\n-   Enables powerful functional patterns like partial application and composition in a type-safe manner.\n\n## ⚠️ Gotchas \u0026 Troubleshooting\n\n-   **Type Inference Limitations:** Always prefer providing explicit `\u003cF, A\u003e` generics to `createNamedArguments` and `\u003cF, A, Spec\u003e` to `createMappedNamedArguments` for best results. Relying on inference can sometimes lead to less precise types, especially with complex functions. Define the `A` type explicitly.\n-   **Function Overloads:** The library works best with single-signature functions. For overloaded functions, pass the *specific overload signature* you want to wrap as the `F` generic type parameter.\n-   **Performance Considerations:** There's a small runtime overhead per argument and per function call compared to direct positional arguments. This is usually negligible but avoid in performance hotspots if necessary.\n-   **Partial Application with Objects (Core Library):** Remember the core `BrandedFunction` tracks applied arguments by *base parameter name*. Partially applying `args.config.host(...)` prevents applying `args.config.port(...)` in a *subsequent* partial call to the *same resulting function*.\n    -   **Workarounds:** Apply related properties in one `.partial()` call, use `reApply`, use the full object setter `args.config({...})` (which typically overwrites), or use `createMappedNamedArguments` for different semantics.\n-   **`as const` for Mapped/Flattening Configs:** When using `createMappedNamedArguments` or optional flattening utilities, always define the configuration object with `as const` and **do not** add an explicit interface type annotation to the variable itself (e.g., `const config = { ... } as const;` not `const config: MyConfigInterface = { ... } as const;`). This preserves required literal types.\n-   **`Cannot find name 'Spec'` / `Type 'X' is not assignable...`:** If you encounter complex type errors, double-check:\n    -   Correct `import` paths.\n    -   Correct use of `as const` for config objects.\n    -   Accurate `A` type provided.\n    -   Restarting your TypeScript server / IDE.\n    -   You are using a compatible TypeScript version.\n\n## Compatibility\n\n-   Requires **TypeScript 4.1+** (for Template Literal Types used internally). Higher versions (4.5+) are recommended for better inference with complex types.\n-   Works with standard JavaScript runtimes (Node, browsers) after TypeScript compilation.\n\n## Contributing\n\nContributions are welcome! Please feel free to open an issue or submit a pull request on the [GitHub repository][github-url].\n\nOkay, here's a detailed API reference including the function signature and a usage example for each key exported function.\n\n---\n\n## 📚 API Reference\n\nThis reference details the primary functions, interfaces, and types exported by the `@doeixd/named-args` library and its associated modules.\n\n---\n\n### **Core Library (`@doeixd/named-args`)**\n\nThese are the fundamental exports for creating and using named arguments.\n\n---\n\n#### `createNamedArguments\u003cF, A\u003e(func, parameters?)`\n\nTransforms a standard function into one accepting named arguments via a generated `args` object and returns a specialized wrapper function (`BrandedFunction`).\n\n*   **Signature:**\n    ```typescript\n    function createNamedArguments\u003c\n      F extends (...args: any[]) =\u003e any,\n      A extends Record\u003cstring, any\u003e = ParamsToObject\u003cParameters\u003cF\u003e\u003e // Provide A explicitly!\n    \u003e(\n      func: F,\n      parameters?: Readonly\u003cParameterInfo[]\u003e\n    ): [NamedArgs\u003cA\u003e, BrandedFunction\u003cF\u003e]\n    ```\n*   **Example:**\n    ```typescript\n    import { createNamedArguments } from '@doeixd/named-args';\n\n    function processUser(id: string, name: string, active: boolean = true) {\n      return { id, name, active };\n    }\n    type ProcessUserArgs = { id: string; name: string; active?: boolean };\n\n    const [args, namedProcessUser] = createNamedArguments\u003c\n      typeof processUser,\n      ProcessUserArgs\n    \u003e(\n      processUser,\n      // Optional metadata for better type safety\n      [ { name: 'id', required: true }, { name: 'name', required: true }, { name: 'active', required: false, defaultValue: true }]\n    );\n\n    const result = namedProcessUser(args.name('Alice'), args.id('u1'));\n    console.log(result); // { id: 'u1', name: 'Alice', active: true }\n\n    const partial = namedProcessUser.partial(args.id('u2'));\n    const result2 = partial(args.name('Bob'), args.active(false));\n    console.log(result2); // { id: 'u2', name: 'Bob', active: false }\n    ```\n\n---\n\n#### `createBuilder\u003cF\u003e(brandedFunc)`\n\nCreates a `Builder` instance for fluently constructing calls to a `BrandedFunction`, preventing duplicate argument application within the chain.\n\n*   **Signature:**\n    ```typescript\n    function createBuilder\u003cF extends (...args: any[]) =\u003e any\u003e(\n      brandedFunc: BrandedFunction\u003cF\u003e\n    ): Builder\u003cF\u003e // Builder has .with(...args) and .execute() methods\n    ```\n*   **Example:**\n    ```typescript\n    import { createNamedArguments, createBuilder } from '@doeixd/named-args';\n\n    function createItem(sku: string, quantity: number, location: string) { /* ... */ }\n    type ItemArgs = { sku: string; quantity: number; location: string };\n\n    const [args, namedCreateItem] = createNamedArguments\u003ctypeof createItem, ItemArgs\u003e(createItem);\n    const itemBuilder = createBuilder(namedCreateItem);\n\n    const item = itemBuilder\n      .with(args.sku('XYZ-123'))\n      .with(args.quantity(100))\n      .with(args.location('Warehouse A'))\n      // .with(args.sku('ABC-456')) // Would log warning at runtime, potentially error if strict\n      .execute();\n\n    // item now holds the result of createItem(...)\n    ```\n\n---\n\n#### `createConfigurableFunction\u003cA, F\u003e([args, brandedFunc])`\n\nCreates a higher-order function factory used to preset arguments for a `BrandedFunction`, useful for creating specialized variants.\n\n*   **Signature:**\n    ```typescript\n    function createConfigurableFunction\u003c\n      A extends Record\u003cstring, any\u003e,\n      F extends (...args: any[]) =\u003e any\n    \u003e(\n      [args, brandedFunc]: [NamedArgs\u003cA\u003e, BrandedFunction\u003cF\u003e]\n    ): (setupFn: (wrappedArgs: NamedArgs\u003cA\u003e) =\u003e void) =\u003e (...remainingArgs: BrandedArg[]) =\u003e CoreReturnType\u003cF\u003e\n    ```\n*   **Example:**\n    ```typescript\n    import { createNamedArguments, createConfigurableFunction } from '@doeixd/named-args';\n\n    function sendNotification(to: string, subject: string, body: string, urgent: boolean = false) { /* ... */ }\n    type NotifyArgs = { to: string; subject: string; body: string; urgent?: boolean };\n\n    const [args, namedNotify] = createNamedArguments\u003ctypeof sendNotification, NotifyArgs\u003e(sendNotification);\n    const configureNotifier = createConfigurableFunction([args, namedNotify]);\n\n    // Create a pre-configured function for urgent notifications\n    const urgentNotifier = configureNotifier(cfgArgs =\u003e {\n      cfgArgs.urgent(true);\n      cfgArgs.subject(\"URGENT NOTIFICATION\");\n    });\n\n    // Use the configured function, providing only remaining args\n    urgentNotifier(args.to('admin@example.com'), args.body('Server down!'));\n    ```\n\n---\n\n#### `createNestedArgs\u003cT\u003e(basePath)`\n\nCreates a proxy object for type-safe access to deeply nested properties of an object parameter.\n\n*   **Signature:**\n    ```typescript\n    function createNestedArgs\u003cT extends Record\u003cstring, any\u003e\u003e(\n      basePath: string\n    ): NestedArgs\u003cT\u003e\n    ```\n*   **Example:**\n    ```typescript\n    import { createNamedArguments, createNestedArgs } from '@doeixd/named-args';\n\n    interface AppConfig { server: { port: number; ssl: { enabled: boolean } }; db: { url: string }; }\n    function setupApp(config: AppConfig) { /* ... */ }\n    type SetupAppArgs = { config: AppConfig };\n\n    const [args, namedSetup] = createNamedArguments\u003ctypeof setupApp, SetupAppArgs\u003e(setupApp);\n    const configArgs = createNestedArgs\u003cAppConfig\u003e('config'); // Proxy for 'config' parameter\n\n    namedSetup(\n      configArgs.server.port(8080),          // Creates BrandedArg with name: \"config.server.port\"\n      configArgs.server.ssl.enabled(true),  // Creates BrandedArg with name: \"config.server.ssl.enabled\"\n      configArgs.db.url(\"...\")             // Creates BrandedArg with name: \"config.db.url\"\n    );\n    ```\n\n---\n\n#### `createObjectPropertyArgs\u003cT\u003e(paramName)`\n\nCreates individual argument accessors for the *first-level* properties of an object parameter.\n\n*   **Signature:**\n    ```typescript\n    function createObjectPropertyArgs\u003cT extends Record\u003cstring, any\u003e\u003e(\n      paramName: string\n    ): Record\u003ckeyof T, NamedArg\u003cany, `${string \u0026 typeof paramName}.${string \u0026 keyof T}`\u003e\u003e // Simplified return type representation\n    ```\n*   **Example:**\n    ```typescript\n    import { createNamedArguments, createObjectPropertyArgs } from '@doeixd/named-args';\n\n    interface ServerOptions { port: number; host: string; }\n    function configureServer(options: ServerOptions) { /* ... */ }\n    type ServerOptsArgs = { options: ServerOptions };\n\n    const [args, namedConfig] = createNamedArguments\u003ctypeof configureServer, ServerOptsArgs\u003e(configureServer);\n    const optionArgs = createObjectPropertyArgs\u003cServerOptions\u003e('options');\n\n    namedConfig(\n      optionArgs.port(9000),   // Creates BrandedArg with name: \"options.port\"\n      optionArgs.host('local') // Creates BrandedArg with name: \"options.host\"\n    );\n    ```\n\n---\n\n#### `isBrandedArg(value)`\n\nType guard to determine if an unknown value is a `BrandedArg`.\n\n*   **Signature:**\n    ```typescript\n    function isBrandedArg\u003cT = unknown, N extends string = string\u003e(\n      value: unknown\n    ): value is BrandedArg\u003cT, N\u003e\n    ```\n*   **Example:**\n    ```typescript\n    import { isBrandedArg } from '@doeixd/named-args';\n    if (isBrandedArg(someValue)) {\n        console.log('Is branded arg:', someValue[BRAND_SYMBOL].name, someValue[BRAND_SYMBOL].value);\n    }\n    ```\n\n---\n\n#### `isBrandedFunction(value)`\n\nType guard to determine if an unknown value is a `BrandedFunction`.\n\n*   **Signature:**\n    ```typescript\n    function isBrandedFunction\u003cF extends (...args: any[]) =\u003e any\u003e(\n      value: unknown\n    ): value is BrandedFunction\u003cF\u003e\n    ```\n*   **Example:**\n    ```typescript\n    import { isBrandedFunction } from '@doeixd/named-args';\n    if (isBrandedFunction(myFunc)) {\n        console.log('Is branded function for:', myFunc._originalFunction.name);\n        console.log('Remaining required:', myFunc.remainingArgs());\n    }\n    ```\n\n---\n\n### **Mapped Arguments Module (`@doeixd/named-args/mapped`)**\n\nProvides an alternative factory for creating named arguments based on an explicit mapping, offering different partial application semantics.\n\n---\n\n#### `createMappedNamedArguments\u003cF, A, Spec\u003e(argMapSpec, func, parameters?)`\n\nCreates a custom `args` object and wrapper function based on an explicit specification map. Allows renaming and mapping to nested paths, with partial application based on the *mapped keys*.\n\n*   **Signature:**\n    ```typescript\n    function createMappedNamedArguments\u003c\n      F extends (...args: any[]) =\u003e any,\n      A extends Record\u003cstring, any\u003e, // MUST provide accurate type A\n      Spec extends ArgMapSpecification // Inferred from const object\n    \u003e(\n      argMapSpec: Spec, // MUST define with 'as const'\n      func: F,\n      parameters?: Readonly\u003cParameterInfo[]\u003e\n    ): [MappedNamedArgs\u003cA, Spec\u003e, MappedBrandedFunction\u003cF, A, Spec, []\u003e]\n    ```\n*   **Example:**\n    ```typescript\n    import { createMappedNamedArguments } from '@doeixd/named-args/mapped';\n\n    function complexTarget(id: string, config: { host: string; port: number }) { /* ... */ }\n    type ComplexTargetArgs = { id: string; config: { host: string; port: number } };\n\n    // Define mapping with 'as const'\n    const spec = {\n      serverId: 'id',\n      hostname: 'config.host',\n      portNum: 'config.port'\n    } as const;\n\n    // Create mapped args and function\n    const [args, mappedFunc] = createMappedNamedArguments\u003c\n      typeof complexTarget,\n      ComplexTargetArgs, // Explicit A\n      typeof spec\n    \u003e(spec, complexTarget);\n\n    // Call using mapped names\n    const partial = mappedFunc.partial(args.hostname('srv1')); // Applied 'hostname' key\n    const final = partial(args.portNum(8080), args.serverId('id1')); // OK to apply 'portNum' (targets same base 'config')\n    const result = final.execute();\n\n    console.log(result); // { id: 'id1', connection: 'srv1:8080', ... }\n    ```\n\n---\n\n### Composability Utilities \n\nProvides functions to transform or combine argument creators (`NamedArg` functions).\n\n---\n\n#### `transformArg(argCreator, transformer)`\n\nApplies a transformation to the input value *before* creating the branded argument.\n\n*   **Signature:**\n    ```typescript\n    function transformArg\u003cT, U, N extends string\u003e(\n      argCreator: NamedArg\u003cT, N\u003e, // e.g., args.timestamp expecting Date\n      transformer: (value: U) =\u003e T // e.g., (v: string) =\u003e new Date(v)\n    ): NamedArg\u003cU, N\u003e // Returns new arg creator expecting string\n    ```\n*   **Example:**\n    ```typescript\n    import { createNamedArguments, transformArg } from '@doeixd/named-args';\n\n    function process(ts: Date) {/*...*/}\n    type ProcessArgs = { ts: Date };\n    const [args, namedProcess] = createNamedArguments\u003ctypeof process, ProcessArgs\u003e(process);\n\n    const dateStringArg = transformArg(args.ts, (str: string) =\u003e new Date(str));\n\n    namedProcess(dateStringArg(\"2024-03-14T10:00:00Z\")); // Pass string, gets converted\n    ```\n\n---\n\n#### `createArgGroup(config)`\n\nGroups related `NamedArg` functions so they can be applied together via a single object.\n\n*   **Signature:**\n    ```typescript\n    function createArgGroup\u003cT extends Record\u003cstring, any\u003e\u003e(\n      config: ArgGroupConfig\u003cT\u003e // Object mapping keys to NamedArg or nested groups\n    ): (values: Partial\u003cT\u003e) =\u003e BrandedArg[] // Function taking values, returning array of BrandedArgs\n    ```\n    *(See `ArgGroupConfig\u003cT\u003e` type definition for details)*\n*   **Example:**\n    ```typescript\n    import { createNamedArguments, createArgGroup } from '@doeixd/named-args';\n\n    function connect(db: { host: string; port: number; user: string; }) {/*...*/}\n    type ConnectArgs = { db: { host: string; port: number; user: string; } };\n    const [args, namedConnect] = createNamedArguments\u003ctypeof connect, ConnectArgs\u003e(connect);\n\n    const dbGroup = createArgGroup({ // Corresponds to 'db' parameter properties\n        host: args.db.host,\n        port: args.db.port,\n        user: args.db.user,\n    });\n\n    namedConnect( ...dbGroup({ host: 'db.local', port: 5432, user: 'app' }) );\n    ```\n\n---\n\n#### `pipeline(argCreator)`\n\nCreates a builder object to chain multiple transformation and filter steps for an argument's value.\n\n*   **Signature:**\n    ```typescript\n    function pipeline\u003cT, U\u003e( // T=initial input, U=final arg type\n      argCreator: NamedArg\u003cU, any\u003e\n    ): ArgumentPipeline\u003cT, U\u003e // Returns pipeline object with .map, .filter, .apply\n    ```\n    *(See `ArgumentPipeline\u003cT, U\u003e` interface definition for details)*\n*   **Example:**\n    ```typescript\n    import { createNamedArguments, pipeline } from '@doeixd/named-args';\n\n    function setRate(rate: number) {/*...*/}\n    type RateArgs = { rate: number };\n    const [args, namedSetRate] = createNamedArguments\u003ctypeof setRate, RateArgs\u003e(setRate);\n\n    const ratePipeline = pipeline\u003cstring, number\u003e(args.rate) // Input string, output number\n      .map(val =\u003e parseFloat(val))\n      .map(val =\u003e Math.max(0, val)) // Ensure non-negative\n      .map(val =\u003e Math.min(1, val)); // Ensure max 1\n\n    namedSetRate(ratePipeline(\"-0.5\")); // Applies rate = 0\n    namedSetRate(ratePipeline(\"1.2\"));  // Applies rate = 1\n    namedSetRate(ratePipeline(\"0.75\")); // Applies rate = 0.75\n    ```\n\n---\n\n#### `combineArgs(targetArg, combiner, ...sourceArgs)`\n\nCreates a \"meta-argument\" function that derives the value for `targetArg` by combining values implicitly passed via `sourceArgs`.\n\n*   **Signature:**\n    ```typescript\n    function combineArgs\u003cT\u003e(\n      targetArg: NamedArg\u003cT, any\u003e, // The arg to set\n      combiner: (sourceValues: any[]) =\u003e T, // Function to combine values\n      ...sourceArgs: NamedArg\u003cany, any\u003e[] // Args providing input to combiner\n    ): () =\u003e BrandedArg\u003cT\u003e[] // Function returning the calculated BrandedArg for target\n    ```\n*   **Example:**\n    ```typescript\n    import { createNamedArguments, combineArgs } from '@doeixd/named-args';\n\n    function drawRect(w: number, h: number, area: number) {/*...*/}\n    type RectArgs = { w: number; h: number; area: number };\n    const [args, namedDraw] = createNamedArguments\u003ctypeof drawRect, RectArgs\u003e(drawRect);\n\n    const autoArea = combineArgs(\n        args.area, // Target\n        ([width, height]) =\u003e width * height, // Combiner\n        args.w, args.h // Sources\n    );\n\n    // Call with width and height, area is added automatically by spreading ...autoArea()\n    namedDraw(args.w(10), args.h(5), ...autoArea());\n    ```\n\n---\n\n#### `withDefault(argCreator, defaultValue)`\n\nCreates a function that, when called with no arguments (`...myDefault()`), produces the `BrandedArg` for `argCreator` using the `defaultValue`.\n\n*   **Signature:**\n    ```typescript\n    function withDefault\u003cT\u003e(\n      argCreator: NamedArg\u003cT, any\u003e,\n      defaultValue: T\n    ): () =\u003e BrandedArg\u003cT\u003e[] // Returns function producing the default BrandedArg in an array\n    ```\n*   **Example:**\n    ```typescript\n    import { createNamedArguments, withDefault } from '@doeixd/named-args';\n\n    function process(data: string, priority?: number) {/*...*/}\n    type ProcessArgs = { data: string; priority?: number };\n    const [args, namedProcess] = createNamedArguments\u003ctypeof process, ProcessArgs\u003e(process);\n\n    const defaultPriority = withDefault(args.priority, 5); // Default priority is 5\n\n    namedProcess(args.data(\"A\")); // priority is undefined\n    namedProcess(args.data(\"B\"), args.priority(10)); // priority is 10\n    namedProcess(args.data(\"C\"), ...defaultPriority()); // priority is 5\n    ```\n\n---\n\n#### `withValidation(argCreator, validator, errorMessage?)`\n\nWraps a `NamedArg` creator, adding runtime validation. Throws an error if the `validator` function returns `false` for the provided value.\n\n*   **Signature:**\n    ```typescript\n    function withValidation\u003cT\u003e(\n      argCreator: NamedArg\u003cT, any\u003e,\n      validator: (value: T) =\u003e boolean,\n      errorMessage?: string\n    ): NamedArg\u003cT, any\u003e // Returns a new validating NamedArg creator\n    ```\n*   **Example:**\n    ```typescript\n    import { createNamedArguments, withValidation } from '@doeixd/named-args';\n\n    function setAge(age: number) {/*...*/}\n    type AgeArgs = { age: number };\n    const [args, namedSetAge] = createNamedArguments\u003ctypeof setAge, AgeArgs\u003e(setAge);\n\n    const validatedAge = withValidation(\n      args.age,\n      (a) =\u003e a \u003e= 0 \u0026\u0026 a \u003c 130, // Validator\n      \"Age must be between 0 and 129\" // Error message\n    );\n\n    namedSetAge(validatedAge(30)); // OK\n    // namedSetAge(validatedAge(150)); // Throws Error: \"Age must be between 0 and 129\"\n    ```\n---\n\n## 📝 License\n\nMIT\n\n---\n[npm-image]: https://img.shields.io/npm/v/@doeixd/named-args.svg?style=flat-square\n[npm-url]: https://npmjs.org/package/@doeixd/named-args\n[build-image]: https://img.shields.io/github/actions/workflow/status/doeixd/named-args/main.yml?branch=main\u0026style=flat-square\n[build-url]: https://github.com/doeixd/named-args/actions?query=workflow%3Amain\n[license-image]: https://img.shields.io/npm/l/@doeixd/named-args.svg?style=flat-square\n[license-url]: https://github.com/doeixd/named-args/blob/main/LICENSE\n[downloads-image]: https://img.shields.io/npm/dm/@doeixd/named-args.svg?style=flat-square\n[downloads-url]: https://npmjs.org/package/@doeixd/named-args\n[github-url]: https://github.com/doeixd/named-args","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoeixd%2Fnamed-arguments","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdoeixd%2Fnamed-arguments","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoeixd%2Fnamed-arguments/lists"}