Pressidian
花园入口
笔记
项目
关于
实验室
GitHub
花园入口
笔记
项目
关于
实验室
GitHub

KNOWLEDGE PATHS

笔记库
当前位置
笔记库/前端/项目笔记/代达罗斯/Anatomy

数组转联合

2 分钟阅读 · Note

目录树 578 篇

            • 定义
            • 交叉类型 & 与联合类型
            • 数组转联合
            • Anatomy包
            • Extract
            • IIFE写法的工厂函数
            • TS类型 extends
          • 项目待做
          • 性能优化
          • UI设计
      • 前端技术栈
    • 笔记目录
    • CLAUDE.md
    • Vue 组件与 Render 函数

关联笔记 6

↗定义同一路径↗交叉类型 & 与联合类型同一路径↗Anatomy包同一路径↗Extract同一路径↗IIFE写法的工厂函数同一路径↗TS类型 extends同一路径
export const AnatomyQuantityValues = [
  "optional",
  "exactly_one",
  "one_or_more",
  "zero_or_more",
] as const;   AnatomyQuantityValues 是个值吗

是的,AnatomyQuantityValues 是一个运行时真实存在的值,具体来说是一个数组:

export const AnatomyQuantityValues = [
  "optional",
  "exactly_one",
  "one_or_more",
  "zero_or_more",
] as const;

你可以直接在代码运行时使用它:

console.log(AnatomyQuantityValues);

AnatomyQuantityValues.map((value) => {
  console.log(value);
});

它的值就是:

[
  "optional",
  "exactly_one",
  "one_or_more",
  "zero_or_more",
]

as const 做了什么

如果不加 as const:

const values = ["optional", "exactly_one"];

TypeScript 通常会把它推断成:

string[]

也就是说,它只知道这是一个字符串数组,不关心里面具体有哪些字符串。

加上 as const:

const values = ["optional", "exactly_one"] as const;

TypeScript 会把它推断得非常精确:

readonly ["optional", "exactly_one"]

这表示:

  1. 数组内容是只读的;
  2. 第一个元素只能是 "optional";
  3. 第二个元素只能是 "exactly_one"。

因此下面这些操作会报错:

AnatomyQuantityValues.push("other");
// 报错:只读数组不能 push

AnatomyQuantityValues[0] = "other";
// 报错:不能修改只读元素

它既可以当值,也可以用来生成类型

你之前看到的:

(typeof AnatomyQuantityValues)[number]

就是从这个数组值中提取元素类型。

先看:

typeof AnatomyQuantityValues

得到的是这个值对应的类型:

readonly [
  "optional",
  "exactly_one",
  "one_or_more",
  "zero_or_more"
]

再加 [number]:

(typeof AnatomyQuantityValues)[number]

表示取这个数组所有数字索引位置可能出现的类型,最终得到:

"optional"
| "exactly_one"
| "one_or_more"
| "zero_or_more"

因此:

quantity?: (typeof AnatomyQuantityValues)[number]

等价于:

quantity?:
  | "optional"
  | "exactly_one"
  | "one_or_more"
  | "zero_or_more";

为什么要这样写

因为可以让“运行时值”和“TypeScript 类型”共用一份数据。

例如运行时可以用它生成下拉选项:

<select>
  {AnatomyQuantityValues.map((value) => (
    <option key={value} value={value}>
      {value}
    </option>
  ))}
</select>

同时又能得到对应的类型:

export type AnatomyQuantity =
  (typeof AnatomyQuantityValues)[number];

然后使用:

const quantity: AnatomyQuantity = "optional"; // 正确

const quantity2: AnatomyQuantity = "many";
// 报错

这样就不用把相同内容写两遍:

// 不推荐:值和类型需要分别维护
type AnatomyQuantity =
  | "optional"
  | "exactly_one"
  | "one_or_more"
  | "zero_or_more";

const AnatomyQuantityValues = [
  "optional",
  "exactly_one",
  "one_or_more",
  "zero_or_more",
];

否则以后增加一个值时,可能只改了数组,却忘了修改类型。

可以记成:

AnatomyQuantityValues

是值;

typeof AnatomyQuantityValues

是这个值的类型;

(typeof AnatomyQuantityValues)[number]

是数组中所有元素组成的联合类型。