先记住这个答案
Uppercase 和 Lowercase 在类型层处理整个字符串的大小写,Capitalize 与 Uncapitalize 处理开头部分。这些内置工具可以参与模板字面量和键名派生,但不会修改运行时字符串。其行为基于 JavaScript 的相应字符串大小写操作,不是区域化文本规则,也不保证字符数量不变;某些 Unicode 字符转大写可能扩展成多个字符。宽 string 参与运算时,也不能得到一份有限名字列表。生成 API 键名应限制并检查命名协议,实际数据转换与本地化显示仍需单独实现。
- 大小写类型工具不会执行运行时字符串转换
- Unicode 转换可能改变长度并造成名称冲突
- 本地化文本规则不能靠内置类型工具完整表达
四种操作分别改变哪些部分
全大写与全小写针对整个输入,开头大小写工具则只处理开头对应部分,并不会遍历每个单词。它们适合约定明确的事件名和方法名派生,让某些拼写关系在类型层保持一致,但不能把所有文本格式化需求都纳入同一个规则。
示例同时包含普通名称与 Unicode 字符。德语字母的转换说明结果长度可能增长,首字符大写也不是简单替换为一个同长度字符。最后的宽字符串约束演示类型仍可以要求大写形式,但它并没有枚举所有可能输入名称。
type EventName = Uppercase<'user-created'>;
type GetterSuffix = Capitalize<'userName'>;
type Expanded = Uppercase<'straße'>;
type InitialExpanded = Capitalize<'ßeta'>;
const event: EventName = 'USER-CREATED';
const upperOnly: Uppercase<string> = 'READY';
// @ts-expect-error 小写字面量不满足大写字符串约束
const rejected: Uppercase<string> = 'ready';EventName 是 USER-CREATED,GetterSuffix 是 UserName,Expanded 是 STRASSE,InitialExpanded 是 SSeta。所有结果来自类型计算;真实变量要改变大小写,仍需执行相应字符串函数。
为什么不能当作本地化字符算法
内置操作并不是根据用户语言区域选择转换规则,也不负责 Unicode 规范化或按用户感知字符簇处理文本。处理土耳其语等有特殊大小写规则的界面内容时,应使用适合的运行时本地化方案,并验证目标语言结果。
即使只做程序键名转换,也要考虑两个源名称转换后相同的情况。大小写折叠和字符扩展可能造成碰撞,映射类型与运行时对象写入又可能采用不同合并方式。因此名字派生需要明确允许字符集和冲突政策,不能把类型通过视为命名唯一性证明。
宽字符串与真实函数返回类型的关系
字面量输入可以得到明确结果,宽 string 则缺少有限候选信息,可能保留类似 Uppercase<string> 的特殊约束。普通字符串方法的声明返回值也不一定自动带有某个精确字面量映射,不能把静态别名与函数返回推断直接画等号。
若封装函数承诺返回某个大小写派生类型,函数体必须真的执行一致转换,并审查边界断言是否合理。对于动态用户文本,过度精确的类型承诺可能得不偿失;更清楚的方式是区分受控标识符生成与一般文本展示,各自使用合适的接口。
容易答错的地方
- 声明 Uppercase 类型后变量会自动变大写
- 类型运算不会修改实际值,输入转换需要真实代码;直接断言为大写类型只是让编译器相信调用者,并没有执行对应操作。
- 大小写转换一定保持字符数且符合所有语言规则
- 部分 Unicode 转换会扩展长度,内置操作也不按用户区域做完整文本处理;生成名称和本地化显示应分别验证边界。
面试官还会怎么问?
Capitalize 会把每个单词的首字母大写吗?
不会,它只针对字符串开头的相应部分,不是标题化文本算法;包含多个单词时需要另外定义运行时处理规则。
为什么宽 string 不能派生有限事件名集合?
它允许无限多字符串,没有提供具体候选名单;要得到有限接口,应从受控字面量联合或静态配置中提取名称,再执行类型变换。
能否用这些类型证明生成的键永远不冲突?
不能,多个源名称可能变换成同一结果;应限制命名协议并在实际构建或注册阶段检测冲突,类型映射不能自动代替完整唯一性验证。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。