如何在TypeScript中为秒和毫秒创建专属类型以避免混用?
TypeScript区分秒/毫秒参数的最佳实践
1. 品牌类型(Branded Types):编译时安全+零运行时开销
这是处理此类语义化数值类型的工业级最佳方案,通过给基本number类型添加唯一标识,让TypeScript在编译时区分不同语义的数值,运行时仍保留普通数字的性能。
// 定义带品牌标识的类型,确保秒和毫秒类型互斥 type Seconds = number & { readonly brand: unique symbol }; type Milliseconds = number & { readonly brand: unique symbol }; // 提供类型转换函数,明确生成对应语义的数值 const toSeconds = (value: number): Seconds => value as Seconds; const toMilliseconds = (value: number): Milliseconds => value as Milliseconds; // 目标函数,接收秒类型参数 function fn(foo: number, elapsedTime: Seconds) { // 业务逻辑中可直接当作number使用 const elapsedMs = elapsedTime * 1000; } // 合法调用 fn(1234, toSeconds(3600)); // 编译通过 // 非法调用(编译报错) fn(1234, 3600); // 错误:number类型无法赋值给Seconds fn(1234, toMilliseconds(3600000)); // 错误:Milliseconds类型无法赋值给Seconds
核心逻辑:利用TypeScript结构类型系统的特性,unique symbol确保每个品牌类型唯一,编译器会拦截跨类型赋值,但运行时不会产生额外代码或性能损耗。
2. 字面量/枚举类型:适合固定参数场景
如果耗时参数只有固定的几个可选值,可直接用字面量类型或枚举限制输入:
// 限定允许传入的秒数值 type AllowedSeconds = 1 | 5 | 30 | 3600; function fn(foo: number, elapsedTime: AllowedSeconds) { // ... } fn(1234, 3600); // 编译通过 fn(1234, 3600000); // 错误:3600000不在AllowedSeconds范围内
优点是简单直接,但灵活性差,无法支持任意合法的秒数值。
3. 类封装:带运行时校验
如果需要在运行时也保证参数的语义正确性,可通过类封装实现:
class Seconds { constructor(public value: number) { // 可选:添加运行时合法性校验 if (value < 0) { throw new Error("秒数不能为负数"); } } } function fn(foo: number, elapsedTime: Seconds) { console.log(elapsedTime.value); } fn(1234, new Seconds(3600)); // 编译通过 fn(1234, 3600); // 错误:number类型无法赋值给Seconds
缺点是会带来运行时开销,适合对参数合法性要求极高的场景。
方案优先级
优先选择品牌类型,它平衡了编译时类型安全、开发体验和运行时性能,是解决此类问题的标准实践。
内容的提问来源于stack exchange,提问作者Evgeniy Pozdnyakov
相关产品推荐
相关产品推荐

