Enquirer 提示类型基类(Prompt Type Classes)完全指南:用 ArrayPrompt、BooleanPrompt、NumberPrompt 与 StringPrompt 打造自定义交互提示

原创2026-09-26 12:21:00153 阅读
文章标签:开发工具

Enquirer 提示类型基类(Prompt Type Classes)完全指南:用 ArrayPrompt、BooleanPrompt、NumberPrompt 与 StringPrompt 打造自定义交互提示

Enquirer 在 lib/types/index.js 中提供了一批低层"起始类"(starter classes),专门用于简化"期望用户返回特定类型响应"的自定义提示(prompt)的创建过程。本文将以 support/src/content/types/index.md 为主体,结合仓库源码逐类剖析 ArrayPrompt、BooleanPrompt、NumberPrompt、StringPrompt 的设计意图、可配置项与按键行为,并演示如何基于它们派生自己的交互提示。读完本文,你将掌握 Enquirer 内置 prompt 的继承体系,并能像写普通类一样快速定制属于自己的终端交互组件。

概览:五类"起始类"

为了让任何用途的提示创建都更简单,Enquirer 提供了几个面向特定响应类型的起始类:

起始类 期望的返回值 对应内置 prompt
ArrayPrompt 一个数组(一项或多项选择值) select、multiselect、scale、survey
BooleanPrompt 布尔值 true / false confirm、toggle
~~DatePrompt~~ 日期相关值(文档中已划线删除,尚未实现) 暂无
NumberPrompt 数值 numeral
StringPrompt 字符串 invisible、list、password

从源码可以确认,lib/types/index.js 实际导出的只有五类:ArrayPrompt、AuthPrompt、BooleanPrompt、NumberPrompt 和 StringPrompt——其中 DatePrompt 并未导出,对应文档中的删除线标记。此外还有一个未在索引文档中展开的 AuthPrompt(认证提示工厂),本文也会一并说明。

在 lib/prompts/ 目录中,绝大多数内置 prompt 正是直接继承这些起始类实现的:

理解了这一点,你就能明白:所谓"自定义 prompt",本质上就是选一个合适的起始类,覆写它的 format、hint、submit、dispatch 等方法。

StringPrompt:一切文本输入的基础

StringPrompt(lib/types/string.js)是返回字符串的提示基类,它直接继承自核心的 lib/prompt.js。它的职责是管理"一行文本输入"的全部状态:input(当前输入内容)、cursor(光标位置)、initial(初始值)以及剪贴板缓冲区 state.clipboard。

核心行为

  • 初始值:构造时会把 initial 强制转成字符串,若存在初始值则隐藏光标直到用户开始输入。
  • 追加与插入:append(ch) 在光标处插入字符并推进光标;insert(str) 是 append 的别名。
  • 删除:delete() 删除光标左侧字符,deleteForward() 删除光标处字符。
  • 剪切与粘贴:cutForward() / cutLeft() 把光标右侧/左侧单词存入 state.clipboard,paste() 再插回。
  • 光标移动:left()/right() 单步移动,first()/last() 跳到首尾,backward()/forward() 是它们的别名。
  • 多行支持:当 options.multiline 为 true 时,按回车会追加换行而不是提交;keypressTimeout 选项则用于控制快速连续回车时的提交行为。

派生提示示例

文档列出的相关提示包括 prompt-split、prompt-text、prompt-invisible、prompt-password。在仓库中:

自定义一个字符串提示,只需要继承 StringPrompt 并覆写 format():

const StringPrompt = require('enquirer/lib/types/string');

class MyTextPrompt extends StringPrompt {
  async format(input = this.value) {
    // 未提交时显示占位符样式,提交后高亮
    return this.state.submitted
      ? this.styles.success(input)
      : this.styles.muted(input);
  }
}

注意:起始类的默认 format() 使用 lib/placeholder.js 渲染占位符——当用户尚未输入时,光标停在 initial 的末尾附近,这是 Enquirer 文本提示"灰字提示 + 可编辑"体验的来源。

NumberPrompt:数值输入基类

NumberPrompt(lib/types/number.js)继承自 StringPrompt,在其基础上加入数值语义。构造时它会解析一组数值选项:

选项 默认值 作用
min -Infinity 允许的最小值
max Infinity 允许的最大值
delay 1000(毫秒) 数字输入后的提交延迟
float true 是否允许小数(false 时取整)
round false 是否四舍五入(float === false 时自动为 true)
major 10 大步进(shift+方向键)
minor 1 小步进(方向键)
initial '' 初始数值

数值特有的行为

  • 步进调整:up()/down() 按 minor 步进,shiftUp()/shiftDown() 按 major 步进;越界时会触发 alert()。
  • 合法性校验:isValue() 用正则 /^[-+]?[0-9]+((\.)|(\.[0-9]+))?$/ 判断输入是否为合法数字;append() 会拦截第二个小数点以及非法符号。
  • 延迟提交:delay 控制数字输入后自动提交的等待时间,配合 submit() 中"从 input 与 initial 中取第一个合法值"的逻辑,实现边输入边校验的体验。
  • 格式化:支持 options.format 函数覆写显示;默认使用 styles.info。

文档提到 NumberPrompt 的"相关提示"为 prompt-confirm 与 prompt-toggle,这应是早期文档的疏漏——从继承关系看,数值类提示的实际代表是 numeral。自定义数值提示只需继承 NumberPrompt 并设置 min/max/step 等约束:

const NumberPrompt = require('enquirer/lib/types/number');

const prompt = new NumberPrompt({
  name: 'age',
  message: '请输入你的年龄',
  min: 0,
  max: 150,
  initial: 18
});

BooleanPrompt:布尔响应基类

BooleanPrompt(lib/types/boolean.js)用于创建返回 true/false 的提示。它的核心是一组判断规则:

  • isTrue(input):匹配 /^[ty1]/i,即 t、y、1(不区分大小写)开头视为真;
  • isFalse(input):匹配 /^[fn0]/i,即 f、n、0 开头视为假;
  • cast(input):调用 isTrue 把原始输入转换为布尔值,value 的 getter 也做同样转换;
  • dispatch(ch):当输入命中 isValue 时立即赋值并 submit()——这就是 confirm 提示"按下 y/n 即提交"的实现原理。

文档明确指出内置的 确认提示 confirm 基于 BooleanPrompt 实现,其"相关提示"为 prompt-confirm 与 prompt-toggle;仓库中 toggle.js 同样继承 BooleanPrompt。

最简单的自定义布尔提示,就是继承后覆写 format 以改变真/假值的显示样式:

const BooleanPrompt = require('enquirer/lib/types/boolean');

class YesNoPrompt extends BooleanPrompt {
  async format(value) {
    return this.state.submitted
      ? this.styles.success(value ? 'yes' : 'no')
      : this.styles.primary(value ? 'yes' : 'no');
  }
}

ArrayPrompt:数组/选择类提示基类

ArrayPrompt(lib/types/array.js)是所有"在终端中显示一组选项并返回一个或多个值"的提示的基类,是起始类中最复杂、功能最丰富的一类。

按键(Keypresses)

文档给出的按键表如下,结合源码可以补全其真实行为:

按键 动作 源码行为
shift+▲ shiftUp 当 options.sort === true 时向上交换选项(swap(index - 1)),否则向上滚动列表
shift+▼ shiftDown 当 options.sort === true 时向下交换选项,否则向下滚动列表
fn+▲(Mac)/ Page Up(Win) pageUp 缩小可视区域 limit(Math.max(limit - 1, 0))并上移光标
fn+▼(Mac)/ Page Down(Win) pageDown 扩大可视区域 limit(Math.min(limit + 1, choices.length))并下移光标

此外还有更多派生提示共用的按键:space 切换选中、a 全选/全不选、i 反选、number(n) 按数字快速跳转选择、g 切换分组、home/end 跳转首尾等。

选项(Options)

名称 类型 默认值 作用
limit Number options.choices.length 终端中同时可见的选项数量,超出部分可滚动浏览
initial Number | String | Array undefined 初始选中的选项索引、名称或名称数组
hint String undefined 附加的提示文本
name String undefined 提示名称,作为结果对象的键
type String undefined 提示类型标识
message String undefined 显示给用户的提示语
choices Array undefined 选项列表,支持字符串、对象、嵌套分组、异步函数等

源码还确认了其他重要选项:multiple(是否多选)、maxSelected(最多可选数量,默认 Infinity)、delay(数字快速选择延迟)、autofocus(自动聚焦项)、sort(是否启用 shift+方向键排序)、scroll(是否允许滚动,false 时首尾按键会触发 alert)。

options.limit 实例

limit 决定同一时刻屏幕上渲染的选项数量。例如下面这个 8 个字母的提示,任何时刻终端只会显示 3 个选项:

const prompt = new Prompt({
  name: 'alphabet',
  message: 'Choose some letters',
  choices: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'],
  limit: 3
});

limit 的默认值取 choices.length,即默认全部展示;实际渲染时会再取 Math.min(limit, this.height),避免超出终端高度。该行为的效果可参考仓库中的演示图:

ArrayPrompt 的 limit 选项演示:8 个选项中仅 3 个可见,其余可滚动浏览

实例属性

  • prompt.choices:由 options.choices 规范化生成的选项数组。源码中 toChoice() 会把字符串转为 { name } 对象,补全 message、value、index、level、indent、path 等字段,并支持函数、Promise 与嵌套分组。
  • prompt.list / prompt.visible:可见选项列表。文档称其为 list,源码中对应 visible getter,返回 (state.visible || choices).slice(0, limit);未设置 limit 时就是整个 choices 数组。
  • prompt.cursor:光标在可见列表中的位置,对应源码中的 index getter(Math.max(0, state.index))。

派生提示

文档列出的相关提示为 prompt-autocompletion、prompt-select、prompt-multiselect、prompt-checkbox、prompt-radio。仓库中 select.js(单选)、scale.js(李克特量表)、survey.js(问卷)均继承 ArrayPrompt,multiselect 相关实现可参考 lib/prompts/multiselect.js。

DatePrompt:文档中已标记"待实现"的日期类型

date.md 以笔记形式记录了 DatePrompt 的规划行为,但它在 lib/types/index.js 的导出列表里并不存在,索引文档中也以删除线 ~~DatePrompt~~ 标注。从源码结构看,这是一个尚未落地的设计,以下内容应视为规划而非当前可用 API:

  • 光标移动:←/→ 移动光标,Alt+B/F 按词移动,Ctrl+A/E 跳到首尾;
  • 数值增减:▲/▼ 修改当前数字位,并具有进位联动——在 "6:59" 增加分钟会使小时进位,在 "23:00" 增加小时会使日期进位,在 "31 Jan" 增加天数会使月份进位,在 "Dec" 增加月份会使年份进位;
  • 提交与取消:Ctrl+C/Escape 取消,Ctrl+J/Return 提交,Ctrl+G 重置,Ctrl+D 退出 REPL;
  • 直接键入数字可修改当前位,输入 1 秒后自动复位,全部数字输入完成后应用并右移光标;
  • 提交前显示问号,提交后显示对勾,取消后显示叉号。

如需在现有版本中实现日期输入,建议基于 StringPrompt 自行派生并实现上述进位规则。

进阶:用 AuthPrompt 工厂创建认证类提示

除了文档列出的五类起始类,lib/types/auth.js 还提供了一个 AuthPrompt 工厂:它继承自 FormPrompt(lib/prompts/form.js),把 authenticate 校验函数注入到 submit() 流程中,并暴露静态方法 create(authenticate) 生成定制类。2FA、账号密码等"先填表单、再统一校验"的提示都可以基于它构建,仓库示例见 examples/enquirer/2-factor-authentication/。

小结

Enquirer 的起始类设计让自定义提示变得异常简单:选定一个返回类型,继承对应基类,覆写少数方法即可获得完整的按键、渲染、校验与提交体系。理解 lib/types/index.js 中各类的继承关系与核心选项,是深入定制 Enquirer 提示的起点;官方文档 prompts.md 与 custom-prompts.md 可作为下一步扩展阅读。若想看到各类型的真实交互效果,仓库 media 目录收录了对应的 GIF 演示。

登录后查看全文
enquirer