# @arrows/multimethod 中文文档

规范地址：https://npmcn.com/package/%40arrows/multimethod/

版本：2\.1\.0
npm 元数据更新时间：2022-04-04T15:35:17.102Z
中文译文更新时间：2026-11-06T20:26:04.109Z
软件包许可：ISC

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40arrows/multimethod
原文对照：https://npmcn.com/package/%40arrows/multimethod/?lang=original
项目主页：https://caderek.github.io/arrows/packages/multimethod/
源码仓库：https://github.com/caderek/arrows/tree/master/packages/multimethod

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：110 kB，63 个文件
- 模块格式：CommonJS
- TypeScript 类型：未声明
- 运行依赖：4 个
- 首次发布：2019-09-17T01:06:22.834Z
- 最新版本发布：2021-10-15T02:06:05.265Z
- 近 30 天下载量：236458（2026-10-07 至 2026-11-05，npm 官方下载统计）
- 已发布版本数：40（近 12 个月 0 个）

## 中文简介

功能多方法库

## 安装

```sh
npm install @arrows/multimethod
```

也可以使用：`pnpm add @arrows/multimethod`、`yarn add @arrows/multimethod`、`bun add @arrows/multimethod`

## README

![arrows - multimethod](https://raw.githubusercontent.com/caderek/arrows/master/assets/arrows-multimethod.svg?sanitize=true)

# @ rows/ 多方法

![npm (scoped)](https://img.shields.io/npm/v/@arrows/multimethod) ![CircleCI](https://img.shields.io/circleci/build/github/caderek/arrows) ![Codecov](https://img.shields.io/codecov/c/github/caderek/arrows?token=c6adb715d638431786fefe69ca08ab00) ![npm bundle size (scoped)](https://img.shields.io/bundlephobia/minzip/@arrows/multimethod) ![GitHub](https://img.shields.io/github/license/caderek/arrows)

## 目录

1.  [导言](https://npmcn.com/package/%40arrows/multimethod/#readme-introduction)
2.  [安装](https://npmcn.com/package/%40arrows/multimethod/#readme-installation)
3.  [使用量](https://npmcn.com/package/%40arrows/multimethod/#readme-usage)
    -   [快速实例](https://npmcn.com/package/%40arrows/multimethod/#readme-quick-example)
    -   [多方法解剖](https://npmcn.com/package/%40arrows/multimethod/#readme-anatomy-of-the-multimethod)
        -   [调度职能](https://npmcn.com/package/%40arrows/multimethod/#readme-dispatch-function)
        -   [大小写值](https://npmcn.com/package/%40arrows/multimethod/#readme-case-value)
        -   [对应值](https://npmcn.com/package/%40arrows/multimethod/#readme-corresponding-value)
    -   [扩展多方法](https://npmcn.com/package/%40arrows/multimethod/#readme-extending-multimethods)
        -   [以单一方法延伸](https://npmcn.com/package/%40arrows/multimethod/#readme-extending-with-a-single-method)
        -   [以多种方法扩展](https://npmcn.com/package/%40arrows/multimethod/#readme-extending-with-multiple-methods)
    -   [方法优先权](https://npmcn.com/package/%40arrows/multimethod/#readme-methods-priority)
    -   [咖喱鱼](https://npmcn.com/package/%40arrows/multimethod/#readme-currying)
        -   [自动咖喱](https://npmcn.com/package/%40arrows/multimethod/#readme-automatic-currying)
        -   [手动咖喱](https://npmcn.com/package/%40arrows/multimethod/#readme-manual-currying)
    -   [定义类型](https://npmcn.com/package/%40arrows/multimethod/#readme-defining-types)
        -   [通过接口添加类型](https://npmcn.com/package/%40arrows/multimethod/#readme-adding-types-via-interfaces)
        -   [通过类型别名和相交相添加类型](https://npmcn.com/package/%40arrows/multimethod/#readme-adding-types-via-type-aliases-and-intersections)
        -   [使用现有类型添加类型](https://npmcn.com/package/%40arrows/multimethod/#readme-adding-types-using-existing-types)
4.  [API 参考](https://npmcn.com/package/%40arrows/multimethod/#readme-api-reference)
    -   [多个](https://npmcn.com/package/%40arrows/multimethod/#readme-multi)
    -   [方法](https://npmcn.com/package/%40arrows/multimethod/#readme-method)
    -   [从多](https://npmcn.com/package/%40arrows/multimethod/#readme-frommulti)
    -   [检查](https://npmcn.com/package/%40arrows/multimethod/#readme-inspect)
    -   [通配符](https://npmcn.com/package/%40arrows/multimethod/#readme-wildcard)
5.  [文章](https://npmcn.com/package/%40arrows/multimethod/#readme-articles)
6.  [许可证](https://npmcn.com/package/%40arrows/multimethod/#readme-license)

## 导言

多方法是具有超能的功能——它们可以做所有普通的功能所能做的,但另外还有:

-   可在提供的论点基础上选择适当的执行,而不明确附加条件的逻辑,
-   很容易扩展,不需要修改原始代码,
-   允许您写出干净、简洁和解码.

多方法库提供了一套微小的更高级的功能来创建强大的,不可变的多方法——在功能上.

图书馆有 **内置类型定义**,提供了极佳的IDE支持.

## 安装

通过国家预防机制:

```sh
npm i @arrows/multimethod
```

维亚·亚恩:

```sh
yarn add @arrows/multimethod
```

## 使用量

_注意: 您可以在 [实例](https://github.com/caderek/arrows/tree/master/packages/multimethod/examples) 文件夹._

### 快速实例

```js
import { multi, method, fromMulti } from '@arrows/multimethod'

/**
 * Save data in a specified format
 *
 * @param {object} data
 * @param {string} format
 * @returns {void}
 */
const save = multi(
  (data, format) => format, // Custom dispatch function

  method('json', (data, format) => {
    console.log('Saving as JSON!')
  }),

  method('html', (data, format) => {
    console.log('Saving as HTML!')
  }),

  method((data, format) => {
    console.log('Default - saving as TXT!')
  }),
)

const data = { name: 'Alice', score: 100 }

save(data, 'json') // -> "Saving as JSON!"
save(data, 'html') // -> "Saving as HTML!"
save(data, 'csv') // -> "Default - saving as TXT!"
```

让我们添加一个新的格式,而不触及现有的代码:

```js
const extendedSave = method('csv', (data, format) => {
  console.log('Saving as CSV!')
})(save)

extendedSave(data, 'json') // -> "Saving as JSON!"
extendedSave(data, 'html') // -> "Saving as HTML!"
extendedSave(data, 'csv') // -> "Saving as CSV!"
extendedSave(data, 'yaml') // -> "Default - saving as TXT!"
```

我们还可以很容易地以多种方法来扩展原始功能:

```js
const extendedSave2 = fromMulti(
  method('csv', (data, format) => {
    console.log('Saving as CSV!')
  }),

  method('yaml', (data, format) => {
    console.log('Saving as YAML!')
  }),
)(save)

extendedSave2(data, 'json') // -> "Saving as JSON!"
extendedSave2(data, 'html') // -> "Saving as HTML!"
extendedSave2(data, 'csv') // -> "Saving as CSV!"
extendedSave2(data, 'yaml') // -> "Saving as YAML!"
```

在这两种情况下,原件为: `save` 函数保持不变.

* * *

这只是个简单的例子,你可以做更多的事情!

### 多方法解剖

```js
const myFunction = multi(
  optional_dispatch_function,

  method(optional_case_value, corresponding_value),

  ...other_methods,
)
```

#### 调度职能

调度函数产生值,应当通过该值发出多方法. 将调度函数的返回值与已登记方法的大小写值进行比较.

匹配算法使用深度严格的等同来找到匹配. 该规则有几种例外,在下文说明 [大小写值](https://npmcn.com/package/%40arrows/multimethod/#readme-case-value) 节.

_说明: 目前执行的深度严格等同算法保证了JSON兼容型的正确结果, `Map`, (中文(简体) ), `Set`,以及 `TypedArray`_

如果不提供发送函数,则使用默认的函数。 默认调度函数将所有参数作为一个数组返回,或者在单个参数的情况下——作为一个独立值返回.

为了收集有用的、随时可用的调度功能,请检查 [@ arrows/ 发送](https://www.npmjs.com/package/@arrows/dispatch) 软件包.

实例:

```js
/**
 * Function with a single argument,
 * default dispatch will return that argument as-is.
 *
 * @param {string} color
 * @returns {string} hex color
 */
const colorToHex = multi(
  method('red', '#ff0000'),
  method('green', '#00ff00'),
  method('blue', '#0000ff'),
)

colorToHex('green') // -> "#00ff00"
```

```js
/**
 * Function with multiple arguments,
 * default dispatch will return the array of arguments.
 *
 * Note that the order of the arguments does matter,
 * so if you want the example to work for each combination,
 * you either have to provide methods for reversed arguments
 * or add your custom dispatch function,
 * which returns sorted values (better option).
 *
 * @param {string} colorA
 * @param {string} colorB
 * @returns {string} the resulting color
 */
const mixLights = multi(
  method(['red', 'green'], 'yellow'),
  method(['red', 'blue'], 'magenta'),
  method(['green', 'blue'], 'cyan'),
)

mixLights('red', 'blue') // -> "magenta"
mixLights('blue', 'red') // -> throws an error
```

```js
/**
 * Function with a custom dispatch.
 * More flexible version of the previous example.
 *
 * @param {string} colorA
 * @param {string} colorB
 * @returns {string} the resulting color
 */
const mixLights = multi(
  (colorA, colorB) => [colorA, colorB].sort(), // custom dispatch
  method(['green', 'red'], 'yellow'),
  method(['blue', 'red'], 'magenta'),
  method(['blue', 'green'], 'cyan'),
)

mixLights('red', 'blue') // -> "magenta"
mixLights('blue', 'red') // -> "magenta"
```

```js
const store = {
  add(text) {
    console.log('todo added')
  },
  remove(id) {
    console.log('todo removed')
  },
  toggle(id) {
    console.log('todo toggled')
  },
}

/**
 * Function with a custom dispatch.
 * Dispatch function can produce any arbitrary value.
 *
 * @param {Object} action
 * @param {Object} store
 * @returns {void}
 */
const handleAction = multi(
  (action, store) => action.type, // custom dispatch
  method('ADD_TODO', (action, store) => store.add(action.text)),
  method('REMOVE_TODO', (action, store) => store.remove(action.id)),
  method('TOGGLE_TODO', (action, store) => store.toggle(action.id)),
)

handleAction({ type: 'ADD_TODO', text: 'Eat banana.' }, store) // -> "todo added"
handleAction({ type: 'TOGGLE_TODO', id: 0 }, store) // -> "todo toggled"
```

#### 大小写值

案例值是两个论点的第一个论据 `method` 如果你只提供一种理由 `method`,将被视为默认情况).

大小写值可以是:

-   普通值
-   构造器/类
-   正则表达式
-   通配符( e)`_`(中文(简体) )
-   上游函数
-   包含以上任何大小写的数组

您可以在一个多方法中混合所有大小写值类型 .

* * *

##### 普通价值

如果大小写值既不是上位函数,正则表达式,构造符也不是通配符,则会使用深度严格的等同算法与调度函数的结果相匹配.

实例:

```js
/**
 * Function with case values as ordinary values.
 * Values can be any arbitrary nested structure, or a primitive.
 *
 * Matched by the deep strict equal algorithm.
 *
 * @param {Object} player
 * @returns {string} greeting
 */
const greet = multi(
  method({ name: 'John', age: '30' }, 'Hello John!'),
  method({ name: 'Jane', age: '25' }, 'Hi Jane!'),
  method('Howdy stranger!'),
)

greet({ name: 'John', age: '30' }) // -> "Hello John!"
greet({ name: 'Jane', age: '25' }) // -> "Hi Jane!"
greet({ name: 'Jane', age: '40' }) // -> "Howdy stranger!"
```

* * *

##### 构造器/类

如果大小写值是构建者,则与发送函数的结果相匹配的是严格的等值(`===`操作员, 如果失败—— `instanceof` 操作员.

示例

```js
class Email {}
class SMS {}

/**
 * Function with case values as constructors.
 * Matched by strict equality check, followed by instanceof check.
 *
 * @param {Object} message
 * @returns {string} status
 */
const sendMessage = multi(
  method(Email, 'Sending email...'),
  method(SMS, 'Sending SMS...'),
)

sendMessage(new Email()) // -> "Sending email..."
sendMessage(new SMS()) // -> "Sending SMS..."

sendMessage(Email) // -> "Sending email..."
sendMessage(SMS) // -> "Sending SMS..."
```

* * *

##### 正则表达式

如果大小写值是正则表达式,则会与发送函数的结果相匹配,即严格等同(`===`操作员, 如果失败—— `RegExp.prototype.test()` 方法.

示例

```js
/**
 * Function with case values as regular expressions.
 * Matched by RegExp.prototype.test() method
 *
 * @param {RegExp} pattern
 * @returns {string} type
 */
const productCategory = multi(
  method(/wine/, 'wine'),
  method(/cheese/, 'cheese'),
  method(/bread/, 'bread'),
)

productCategory('blue cheese') // -> "cheese"
productCategory('red wine') // -> "wine"
productCategory('white wine from Germany') // -> "wine"
productCategory('breadcrumbs') // -> "bread"
```

* * *

##### 通关卡

如果大小写值是通配符(`_`它符合任何值.

当需要检查参数的正确性时,或者在(当多方法有多个有效签名时)绕来绕去时,Wildcards(尤其是与上游函数配对)非常有用.

Wildcard在功能上相当于总是返回真实的上方函数: `() => true`。它速度要快一点,因为它不会执行不必要的功能.

示例

```js
const { multi, method, _ } = require('@arrows/multimethod')

/**
 * Function with case values containing wildcards.
 * These values always resolve to true.
 *
 * @param {any} a
 * @param {any} b
 * @param {any} c
 * @returns {string}
 */
const checkArgs = multi(
  (...args) => args.map((arg) => typeof arg),

  // Skipping check on the first argument
  method([_, 'function', 'function'], () => {
    throw new Error('To many functions')
  }),

  // Skipping check on the second argument
  method(['object', _, 'function'], () => {
    throw new Error('Wrong combination')
  }),

  method((a, b, c) => 'ok'),
)

checkArgs(
  1,
  () => 2,
  () => 3,
) // -> Error: To many functions
checkArgs({ id: 1 }, 2, () => 3) // -> Error: Wrong combination
checkArgs(1, { id: 2 }, () => 3) // -> "ok"
```

* * *

##### 优先函数

如果大小写值是函数,则与发送函数的结果相匹配的是严格的等值(`===`操作员, 如果失败了 如果失败了 原始调度功能将被忽略, ,并且将使用所有提供的理由执行大小写值函数.

大小写值函数应返回布尔值(或至少输出将被视为布尔值).

如果返回值是真实的,那么我们有一个匹配.

示例

```js
/**
 * Function with case values as ordinary functions.
 * Case value functions will be executed ignoring dispatch function values,
 * and instead operating on raw arguments.
 *
 * It's like each method has its own dispatch function.
 *
 * It is useful when you can't express dispatch in one common function
 * when each case has some specific rule.
 *
 * @param {Object} req
 * @returns {string | Object} response value
 */
const router = multi(
  method(
    (req) => ['GET'].includes(req.method) && req.url === '/',
    'Hello world!',
  ),

  method(
    (req) => ['GET', 'POST'].includes(req.method) && req.url === '/users',
    [{ id: 1, name: 'John' }],
  ),

  method('Oops!'),
)

router({ method: 'GET', url: '/' }) // -> "Hello world!"
router({ method: 'POST', url: '/' }) // -> "Oops!"
router({ method: 'GET', url: '/users' }) // -> [{ id: 1, name: "John" }]
```

##### 在数组内预先指定函数

在一个数组内部的先入为主的函数有稍有不同的行为,它们作为参数收到调度函数在当前位置提供的值. 它们不会收到所有参数,也不会推翻调度功能.

这样你就可以微调每个争论的检查, 超越简单的平等检查.

示例

```js
const { multi, method, _ } = require('@arrows/multimethod')

/* Custom predicates */
const not = (y) => (x) => x !== y
const notIn = (...args) => (x) => !args.includes(x)

/**
 * Function with case value ans an array
 * that contains predicate functions.
 *
 * Very powerful when paired with wildcards.
 *
 * @param {any} a
 * @param {any} b
 * @returns {string}
 */
const checkArgs2 = multi(
  (a, b) => [typeof a, typeof b],

  method([not('number'), _], () => {
    throw new Error('First argument should be a number')
  }),

  method([_, notIn('string', 'number')], () => {
    throw new Error('Second argument should be a number or a string')
  }),

  method((a, b) => 'ok'),
)

checkArgs2('a', 1) // -> Error: First argument should be a number
checkArgs2(1, [2, 3]) // -> Error: Second argument should be a number or a string
checkArgs2(1, 2) // -> "ok"
checkArgs2(5, 'b') // -> "ok"
```

* * *

##### 有特殊情况的矩阵

如果大小写值是一个包含其中任一的数组:

-   构造器/类
-   正则表达式
-   通配符( e)`_`(中文(简体) )
-   上游函数

然后,这些值将根据其特定的算法相匹配(但只有在它们处于数组的第一级时).

其它的都会使用深度严格的等同算法进行匹配.

这使得您可以使用最适合每个参数的参数发送多个参数.

示例

```js
class Article {}
class Recipe {}

class PDF {}
class HTML {}

/**
 * Function with case values as constructors wrapped in an array.
 *
 * It can be very useful, for example as a trivial alternative
 * to otherwise complex visitor patter.
 *
 * @param {Object} document
 * @param {Object} template
 * @returns {string} embedding description
 */
const embed = multi(
  method([Article, PDF], 'Embedding article inside PDF'),
  method([Article, HTML], 'Embedding article inside HTML'),
  method([Recipe, PDF], 'Embedding recipe inside PDF'),
  method([Recipe, HTML], 'Embedding recipe inside HTML'),
)

embed(new Article(), new PDF()) // -> "Embedding article inside PDF"
embed(new Recipe(), new HTML()) // -> "Embedding recipe inside HTML"
```

你可能也注意到我已经在通配符和上游函数示例中使用了这种方法!

* * *

在一个多方法中不同类型大小写值的额外示例 :

```js
const VIPs = ['john@vip.com', 'alice@vip.com']

/**
 * Function with case values of different types.
 *
 * @param {Object} msg
 * @returns {string | Object} response value
 */
const notify = multi(
  (msg) => msg.type,

  method(
    (msg) => msg.type === 'email' && VIPs.includes(msg.email),
    'Email from VIP!',
  ),

  method('email', (msg) => `Email from ${msg.email}!`),
  method('sms', (msg) => `SMS from ${msg.number}!`),
)

notify({ type: 'email', email: 'alice@vip.com' }) // -> "Email from VIP!"
notify({ type: 'email', email: 'joe@ab.com' }) // -> "Email from joe@ab.com!"
notify({ type: 'sms', number: '123456789' }) // -> "SMS from 123456789!"
```

#### 对应值

相应的值是二分法的第二个参数 `method` (或者默认方法的第一个也是唯一的论据).

对应值可以是:

1.  函数
2.  任何其他价值

如果对应的值是一个函数,则当其大小写值匹配时,会用经过的参数执行.

如果对应的值不是函数,则当其大小写值匹配时会返回.

已经有许多例子,但还有另一个例子:

```js
/**
 * Classic recursive fibonacci.
 * Do not use it for big numbers.
 *
 * As you can see, recursion works just like with ordinary functions.
 *
 * @param {number} n
 * @returns {number} Nth element of fibonacci sequence
 */
const fib = multi(
  method(0, 0),
  method(1, 1),
  method((n) => fib(n - 1) + fib(n - 2)),
)

fib(0) // -> 0
fib(1) // -> 1
fib(9) // -> 34
```

### 扩展多方法

多相方法是不可改变的,所以我们不能增加/克服方法,但是我们可以轻松地在现有的方法的基础上创建一个新的多相方法. 事实上,每次我们执行 `method` 函数,我们创建了新的多方法。 这种多式方法将拥有旧有的所有案例,加上新方法(你也可以通过使用同一种案例Value作为现有方法来"替换"一个方法).

#### 以单一方法延伸

您可以通过执行创建新的多方法 `method` 函数,并传递一个碱基多方法作为第二个块的参数.

实例:

```js
const baseAdd = multi(
  (a, b) => [typeof a, typeof b],
  method(['number', 'number'], (a, b) => a + b),
  method(['string', 'string'], (a, b) => `${a}${b}`),
)

/**
 * Creating a new multimethod with an additional method
 */
const add = method(['bigint', 'bigint'], (a, b) => a + b)(baseAdd)

add(1, 2) // -> 3
add('bat', 'man') // -> "batman"
add(1n, 2n) // -> 3n
```

```js
const { multi, method } = require('@arrows/multimethod')

/**
 * @param {string} language
 * @returns ${string} greeting
 */
const baseGreet = multi(
  method('ru', 'Привет!'),
  method('es', '¡Hola!'),
  method('pl', 'Cześć!'),
)

/**
 * Creating a new multimethod with added default method
 */
const greet = method('Hello!')(baseGreet)

greet('ru') // -> "Привет!"
greet('es') // -> "¡Hola!"
greet('pl') // -> "Cześć!"
greet('fr') // -> "Hello!"
```

#### 以多种方法扩展

您可以用通用方法创建多种新方法 `compose` 函数,或使用内置 `fromMulti` 函数,可以给您额外的类型检查和更好的错误处理.

实例:

```js
const baseHandleHTTPError = multi(
  method(400, 'Incorrect request.'),
  method(404, 'The path does not exist.'),
)

/**
 * Creating a new multimethod with many new methods via generic functional `compose`
 */
const handleHTTPError = compose(
  method(403, 'You do not have access to this resource.'),
  method(418, 'We are all teapots!'),
)(baseHandleHTTPError)

handleHTTPError(400) // -> "Incorrect request."
handleHTTPError(418) // -> "We are all teapots!"
```

```js
const baseArea = multi(
  (shape) => shape.type,
  method('rectangle', (shape) => shape.a * shape.b),
  method('square', (shape) => shape.a ** 2),
)

/**
 * Creating a new multimethod with many new methods via `fromMulti` function
 */
const area = fromMulti(
  method('circle', (shape) => Math.PI * shape.r ** 2),
  method('triangle', (shape) => 0.5 * shape.a * shape.h),
)(baseArea)

area({ type: 'square', a: 5 }) // -> 25
area({ type: 'circle', r: 3 }) // -> 28.274333882308138
```

### 方法优先权

当你执行 `method` 函数,该大小写将添加到多方法的前端。 如果部分适用 `method` 转到 `multi` 财务报告和审定财务报表 `fromMulti` 函数,它们将被从下到上添加到上 - 这样它们就能在最后的多方法中保持顺序 .

多方法内方法的顺序决定其优先性.

实例:

```js
/** Priorities for methods inside `evenMoreExtended` function */

const base = multi(
  method('a', 'priority: 5'),
  method('b', 'priority: 6'),
  method('c', 'priority: 7'),
)

const extended = method('d', 'priority: 4')(base)

const evenMoreExtended = fromMulti(
  method('e', 'priority: 1'),
  method('f', 'priority: 2'),
  method('g', 'priority: 3'),
)(extended)
```

```js
/**
 * Function where the priority does matter
 *
 * @param {number} points
 * @returns {number} grade
 */
const baseGradeExam = multi(
  method((points) => points < 10, 'failed'),
  method((points) => points <= 15, 'ok'),
  method((points) => points > 15, 'good'),
)

const gradeExam = fromMulti(
  method((points) => points === 0, 'terrible'),
  method((points) => points > 20, 'excellent'),
)(baseGradeExam)

gradeExam(0) // -> 'terrible'
gradeExam(5) // -> 'failed'
gradeExam(10) // -> 'ok'
gradeExam(15) // -> 'ok'
gradeExam(20) // -> 'good'
gradeExam(25) // -> 'excellent'
```

### 咖喱鱼

多方法支持以调度功能为基础的自動咖喱和手动咖喱/克通克.

#### 自动咖喱

多种方法与所有通用 `curry` 函数。 currying 是基于调度函数的长度,所以您应该提供明确的调度(默认调度是一个variadic函数,所以它的长度是 `0`) (中文(简体) ).

示例

```js
import curry from '@arrows/composition/curry'
import { multi, method } from '@arrows/multimethod'

/**
 * Currying with a generic `curry` function.
 * Explicit dispatch function (length: 2).
 *
 * Note that you can still use non-functions as corresponding values.
 *
 * @param {string} type
 * @param {string} source
 * @returns {string} info
 */
const play = multi(
  (type, source) => type,
  method('audio', (type, source) => `Playing audio from: ${source}`),
  method('video', (type, source) => `Playing video from: ${source}`),
)

const curriedPlay = curry(play)

const playAudio = curriedPlay('audio')
const playVideo = curriedPlay('video')

playAudio('songs.io/123') // -> "Playing audio from: songs.io/123"
playVideo('movies.com/123') // -> "Playing video from: movies.com/123"
```

#### 手动咖喱

如果你更喜欢手动咖喱,或者已经拥有使用手动咖喱并想用来作为方法的功能,那么用多方法就很容易做到. 就像自动咖喱一样,执行是基于调度功能.

实例:

```js
const mapArray = (fn) => (arr) => arr.map(fn)
const mapString = (fn) => (str) => [...str].map(fn)

/**
 * Manually curried function - each chunk has one argument.
 */

const map = multi(
  (fn) => (val) => (Array.isArray(val) ? 'array' : typeof val),
  method('array', mapArray),
  method('string', mapString),
)

map((char) => char.charCodeAt(0))('Hello') // -> [72, 101, 108, 108, 111]
map((item) => item * 2)([1, 2, 3]) // -> [2, 4, 6]
```

```js
/**
 * Manually chunked function - chunks can have more than one argument,
 * they can also have default values.
 *
 * Note that you can still use non-functions as corresponding values.
 */
const checkGrammar = multi(
  (config = {}) => (text, language) => language,
  method('en', 'Checking English grammar'),
  method('pl', 'Checking Polish grammar'),
)

const checkTypos = checkGrammar({ typos: true })

checkTypos('mistkae', 'en') // -> "Checking English grammar"
checkTypos('błąt', 'pl') // -> "Checking Polish grammar"
```

* * *

_注:为了能够做到这一点 `multi` 函数在创建多方法时计数块(块),在返回的值不是函数或出错之前执行不设参数的调度函数。 这有一个限制 **您不应该使用任何基于参数的计算作为默认值**。如果你这样做,图书馆将无法正确计算段数._

_例如,这不会如意:_

```js
/**
 * This won't work, because of the default value calculation
 * based on the `a` argument.
 */
const fn = multi(
  (a) => (b = a.foo) => a,
  method(/* some case */),
  method(/* some case */),
)
```

* * *

_注:如果在使用手动曲折时使用自定义的大小写Value函数,这些小写Value函数的参数会自动被平分._

_例如:_

```js
/**
 * Even though we don't use dispatch value, we have to add
 * a dispatch function to inform the multimethod of how to handle this.
 *
 * As you can see, you don't have to curry a caseValue function.
 */
const fn = multi(
  a => b => c => null,
  method((a, b, c) => /* some check */, a => b => c => /* some return */),
  method((a, b, c) => /* some check */, a => b => c => /* some return */),
)
```

### 定义类型

如果您使用 TypeScript, 您可能想要使您的多方法类型安全 。 虽然多方法的动态性质使得编译器很难保证多方法的正确组合,但您仍然可以通过定义其界面来确保多方法的正确使用.

TypeScript支持通过接口(或类型别名等同)和相交方式定义的函数超载.

### 通过接口添加类型

从零开始创建新的多方法 :

```ts
import { method, multi, Multi } from '@arrows/multimethod'

/**
 * Multimethod type defined using interface.
 *
 * Extending `Multi` type guarantees that the multimethod will
 * be recognized correctly when extended by `fromMulti` function.
 */
interface Add extends Multi {
  (x: number, y: number): number
  (x: string, y: string): string
}

const add: Add = multi(
  <T>(x: T, x: T): T => [x, y],
  method([Number, Number], (x: number, y: number): number => x + y),
  method([String, String], (x: string, y: string): string => `${x}${y}`),
)

/*
add(3, 3) // -> 6
add('foo', 'bar') // -> "foobar"
add(5, [1, 2]) // compiler error
*/

export { Add }
export default add
```

从现有方法创建新的多方法:

```ts
import { fromMulti, method } from '@arrows/multimethod'
import baseAdd, { Add as BaseAdd } from './add'

/**
 * Multimethod type defined by extending existing interface.
 */
interface Add extends BaseAdd {
  (x: bigint, y: bigint): bigint
}

const add: Add = fromMulti(
  method([BigInt, BigInt], (x: bigint, y: bigint) => x + y),
)(baseAdd)

add(3, 3) // -> 6
add('foo', 'bar') // -> "foobar"
add(3n, 5n) // -> 8n
add(3, [1, 2, 3]) // compiler error
```

### 通过类型别名和相交相添加类型

从零开始创建新的多方法 :

```ts
import { method, multi, Multi } from '@arrows/multimethod'

/**
 * Multimethod type defined using interface.
 *
 * Using intersection with `Multi` type guarantees that the multimethod
 * will be recognized correctly when extended by `fromMulti` function.
 */
type Add = Multi & {
  (x: number, y: number): number
  (x: string, y: string): string
}

const add: Add = multi(
  <T>(x: T, x: T): T => [x, y],
  method([Number, Number], (x: number, y: number): number => x + y),
  method([String, String], (x: string, y: string): string => `${x}${y}`),
)

/*
add(3, 3) // -> 6
add('foo', 'bar') // -> "foobar"
add(5, [1, 2]) // compiler error
*/

export { Add }
export default add
```

从现有方法创建新的多方法:

```ts
import { fromMulti, method } from '@arrows/multimethod'
import baseAdd, { Add as BaseAdd } from './add'

/**
 * Multimethod type defined by intersection with existing type.
 */
type Add = BaseAdd & {
  (x: bigint, y: bigint): bigint
}

const add: Add = fromMulti(
  method([BigInt, BigInt], (x: bigint, y: bigint) => x + y),
)(baseAdd)

add(3, 3) // -> 6
add('foo', 'bar') // -> "foobar"
add(3n, 5n) // -> 8n
add(3, [1, 2, 3]) // compiler error
```

#### 使用现有类型添加类型

通常,创建多方法的最好方法是首先定义组件函数及其类型别名——这样你就可以使用多方法或原始函数——按照什么最合适. 这样也可以重新使用多方法类型特定部件的型号别名.

从零开始创建新的多方法 :

```ts
import { method, multi, Multi } from '@arrows/multimethod'

/**
 * Component functions with type aliases.
 */
type AddNumbers = (x: number, y: number) => number
const addNumbers: AddNumbers = (x, y) => x + y

type AddStrings = (x: string, y: string) => string
const addStrings: AddStrings = (x, y) => `${x}${y}`

/**
 * Multimethod type defined by intersection of existing types.
 *
 * Using intersection with `Multi` type guarantees that the multimethod
 * will be recognized correctly when extended by `fromMulti` function.
 */
type Add = Multi & AddNumbers & AddStrings

const add: Add = multi(
  <T>(x: T, y: T): T => [x, y],
  method([Number, Number], addNumbers),
  method([String, String], addStrings),
)

/*
add(3, 3) // -> 6
add('foo', 'bar') // -> "foobar"
add(3, [1, 2, 3]) // compiler error
*/

export { addNumbers, addStrings, Add }
export default add
```

从现有方法创建新的多方法:

```ts
import { fromMulti, method } from '@arrows/multimethod'
import baseAdd, { Add as BaseAdd } from './add'

type AddBigInts = (x: bigint, y: bigint) => bigint
const addBigInts: AddBigInts = (x, y) => x + y

/**
 * Multimethod type defined by intersection of existing types.
 */
type Add = BaseAdd & AddBigInts

// prettier-ignore
const add: Add = fromMulti(
  method([BigInt, BigInt], addBigInts),
)(baseAdd)

add(3, 3) // -> 6
add('foo', 'bar') // -> "foobar"
add(3n, 5n) // -> 8n
add(3, [1, 2, 3]) // compiler error
```

## API 参考

### 多个

创建多方法 - 一个能够动态选择正确执行的函数, 依据是任意发出其论点.

#### 参数

-   `first` - 第一个论据可以是发送函数,也可以是部分应用的方法
-   `methods` - 部分应用方法的任意数量(可选)

#### 回返

-   返回可用作普通函数的不可改变多方法

#### 接口

```

(dispatch | method, method?, method?, ..., method?) => multimethod

```

#### 实例

创建自定义发送和无方法的多方法 :

```javascript
const fn = multi((x) => typeof x)

fn('foo') // -> throws an Error (because of no matching methods), but is useful as a base for extensions
```

创建带有默认调度和一些方法的多方法 :

```javascript
const makeSound = multi(
  method('cat', () => 'Meow!'),
  method('dog', () => 'Woof!'),
  method(() => 'Hello!'), // default method (ony one argument provided)
)

makeSound('cat') // -> 'Meow!'
makeSound('dog') // -> 'Woof!'
makeSound('cow') // -> 'Hello!' (that's a rather unusual cow)
```

创建自定义调度和一些方法的多方法 :

```javascript
const multiply = multi(
  (multiplier, x) => [typeof multiplier, typeof x],
  method(['number', 'number'], (multiplier, x) => x * multiplier),
  method(['number', 'string'], (multiplier, x) => x.repeat(multiplier)),
)

multiply(2, 5) // -> 10
multiply(3, 'Beetlejuice! ') // -> 'Beetlejuice! Beetlejuice! Beetlejuice! ' (do not read it out loud)
multiply(2, [1, 2, 3]) // -> throws an Error (no match and no default method for these arguments)
```

* * *

### 方法

将方法添加到多方法

#### 参数

-   `caseValue` - 调度函数结果的相配值
    
    -   如果函数,则使用输入参数执行(即使发送被块化,也总是被卸载,
    -   如果是构造器,则首先以引用值相匹配,如果失败,则以操作者为例.
-   `correspondingValue` - 该函数在匹配时应当返回的值
    
    -   如果函数随输入参数而执行(根据发送函数而切入或未切入)
-   `multimethod` - 新的多方法基础的多方法
    

#### 回返

-   新的多方法( 基准方法不变 )

#### 接口

```
(caseValue?, correspondingValue) => (multimethod) => new_multimethod
```

#### 实例

作为函数的默认方法 :

```javascript
const sayHello = multi((user) => user.lang)

const sayHelloWithDefault = method((user) => `Hello ${user.name}!`)(sayHello)

sayHelloWithDefault({ name: 'Alejandro', lang: 'es' }) // -> 'Hello Alejandro!'
```

默认方法作为其它值 :

```javascript
const sayHello = multi((user) => user.lang)

const sayHelloWithDefault = method('Hello!')(sayHello)

sayHelloWithDefault({ name: 'Alejandro', lang: 'es' }) // -> 'Hello!'
```

以大小写值为普通值和以相应值为函数的方法:

```javascript
const add = multi((a, b) => [typeof a, typeof b])

const extendedAdd = method(['number', 'number'], (a, b) => a + b)(add)

extendedAdd(1, 2) // -> 3
```

将大小写值和相应的值作为普通值的方法:

```javascript
const getHexColor = multi((color) => color)

const extendedGetHexColor = method('red', '#FF0000')(getHexColor)

extendedGetHexColor('red') // -> '#FF0000'
```

以大小写值为函数、以相应值为普通值的方法:

```javascript
class Enemy {}
const is = (prototype) => (value) => value instanceof prototype

const greet = multi((person) => person)

// Matches, when case function executed with initial arguments returns truthy value
const extendedGreet = method(is(Enemy), 'Goodbye!')(greet)

extendedGreet(new Enemy()) // -> 'Goodbye!'
```

以大小写值和相应值为功能的方法:

```javascript
class Car {
  drive() {
    return 'driving...'
  }
}

class Human {
  walk() {
    return 'walking...'
  }
}

const is = (prototype) => (value) => value instanceof prototype

const go = multi(
  method(is(Car), (entity) => entity.drive()), // lets add one case to original multimethod
)

const extendedGo = method(is(Human), (entity) => entity.walk())(go)

extendedGo(new Car()) // -> 'driving...'
extendedGo(new Human()) // -> 'walking...'
```

将大小写作为构建者的方法:

```javascript
class Email {}
class SMS {}

const send = multi(
  method(Email, 'Sending email...'), // lets add one case to the original multimethod
)

const extendedSend = method(SMS, 'Sending SMS...')(send)

extendedSend(new Email()) // -> 'Sending email...'
extendedSend(new SMS()) // -> 'Sending SMS...'
```

* * *

### 从多

从现有方法创建新的多方法,方便添加多方法.

#### 参数

-   `methods` - 部分应用方法的任意数量
-   `multimethod` - 您想要基于的多方法

#### 回返

-   新的多方法( 基本方法不变 )

#### 接口

```
(method, method?, ..., method?) => (multimethod) => new_multimethod
```

#### 示例

以现有方法为基础创建新的多方法:

```javascript
const add = multi(
  (a, b) => [typeof a, typeof b],
  method(['number', 'number'], (a, b) => a + b),
  method(['string', 'string'], (a, b) => `${a}${b}`),
)

const extendedAdd = fromMulti(
  method(['bigint', 'bigint'], (a, b) => a + b),
  method(['number', 'bigint'], (a, b) => BigInt(a) + b),
  method(['bigint', 'number'], (a, b) => a + BigInt(b)),
)(add)

extendedAdd(1, 2) // -> 3
extendedAdd('foo', 'bar') // -> 'foobar'
extendedAdd(2n, 3n) // -> 5n
extendedAdd(5, 5n) // -> 10n
extendedAdd(9n, 2) // -> 11n
```

* * *

### 检查

用多方法的构件获取对象.

_注意: 您不应该改变其任何值 ._

#### 参数

-   `multimethod` - 要检查的多方法

#### 回返

-   带有下列精度的检查对象:
    -   `dispatch` - 显示调度功能
    -   `entries` - 显示所有 `[caseValue, correspondingValue]` 条目
    -   `cases` - 显示所有 `caseValue` 项目
    -   `values` - 显示所有 `correspondingValue` 项目

#### 接口

```
(multimethod) => inspection_object
```

#### 示例

```javascript
const { multi, method, inspect, _ } = require('@arrows/multimethod')

const mixColors = multi(
  method(['yellow', 'red'], 'orange'),
  method(['black', _], 'black'),
  method(['red', 'blue'], 'purple'),
  method('no idea'),
)

const inspectionObject = inspect(mixColors)

console.dir(inspectionObject.entries, { depth: null })

/* Result:

[
  [ { type: 'data', value: [ 'yellow', 'red' ] }, 'orange' ],
  [
    {
      type: 'mixed',
      values: [ { type: 'data', value: 'black' }, { type: 'wildcard' } ]
    },
    'black'
  ],
  [ { type: 'data', value: [ 'red', 'blue' ] }, 'purple' ],
  [ { type: 'default' }, 'no idea' ]
]

*/

console.dir(inspectionObject.dispatch, { depth: null })

/* Result:

[Function: implicitDispatch]

*/
```

* * *

### 通配符

符合任何值的特殊占位符 .

#### 示例

```javascript
const { multi, method, _ } = require('@arrows/multimethod')

const hasCallback = multi(
  (a, b) => [typeof a, typeof b],
  method(['function', __], true),
  method([__, 'function'], true),
  method(false),
)

hasCallback(() => {}, 'a') // -> true
hasCallback('b', () => {}) // -> true
hasCallback(
  () => {},
  () => {},
) // -> true
hasCallback(3, 4) // -> false
```

## 文章

-   _通过多方法消除物体的多态性_ 叶霍纳坦·沙维特 [链接](https://blog.klipse.tech/javascript/2021/10/03/multimethod.html)

## 许可证

项目处于开放式、非限制性 [ISC 许可证](https://npmcn.com/package/%40arrows/multimethod/LICENSE)。 。 。 .

## 常见问题

### @arrows/multimethod 是什么？

功能多方法库

### 如何安装 @arrows/multimethod？

在项目目录运行 npm install @arrows/multimethod，或使用 pnpm add / yarn add / bun add。

### @arrows/multimethod 支持 TypeScript 吗？

该包的 package\.json 没有声明类型入口。需要类型提示时，可以在 npm 上查找社区维护的 @types/arrows\_\_multimethod 是否存在。

### @arrows/multimethod 支持 ESM 还是 CommonJS？

以 CommonJS 形式发布，可以直接 require\(\)；在 ES Module 项目中也可以用 import 引入（Node\.js 会把 module\.exports 作为默认导出）。

### @arrows/multimethod 可以商用吗？

@arrows/multimethod 使用 ISC 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @arrows/multimethod 的下载量有多少？

根据 npm 官方下载统计，@arrows/multimethod 在 2026/10/07 – 2026/11/05 这 30 天内被下载 236,458 次，日均约 7,882 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @arrows/multimethod 还在维护吗？

最新版本 2\.1\.0 发布于 2021/10/15（5 年前）；至今共发布 40 个版本，其中过去 12 个月发布了 0 个；首次发布于 2019/09/17。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

