🔌 TypeScript 魔法:一行代码生成从 1 开始编号的 IoT 设备字典
在物联网(IoT)项目中,我们经常需要为大量设备生成严格类型化的数据模型。今天分享一个超实用的 TypeScript 技巧:利用模板字面量 + 递归元组 在编译期自动生成「IOT1、IOT2 ... IOTN」的键值对象,并保证从 1 开始编号!
🎯 场景痛点
假设你要给 100 个温度传感器建一个字典,键名必须是 T1
~ T100
,值是 number
:
ts
const data = {
T1: 23.7,
T2: 24.1,
// ... 手写 100 个键?😱
};
手写不仅累,还容易错。我们希望:
- 编译期就能校验键名范围
- 从 1 开始而不是 0
- 一行类型搞定,拒绝运行时魔法
🧙♂️ 解法:类型体操三步走
Step 1: 构造指定长度的元组
ts
type BuildTuple<L extends number, T extends any[] = []> =
T['length'] extends L
? T
: BuildTuple<L, [...T, any]>;
BuildTuple<3>
→[any, any, any]
Step 2: 把元组索引变成字符串并 +1
ts
type TupleIndices<T extends readonly any[]> = keyof T & `${number}`;
type AddOne<S extends string> =
S extends `${infer N extends number}`
? `${[...BuildTuple<N>, any]['length']}` // N + 1 并转回字符串
: never;
Step 3: 生成最终对象类型
TypeScript
ts
type CreateIotData<
Prefix extends string,
ValueType,
Count extends number
> = {
[K in TupleIndices<BuildTuple<Count>> as `${Prefix}${AddOne<K>}`]: ValueType;
};
🚀 实战演示
1. 定义 5 个智能灯泡的亮度
TypeScript
ts
type Bulbs = CreateIotData<'Light', number, 5>;
// 等价于:
// type Bulbs = {
// Light1: number;
// Light2: number;
// Light3: number;
// Light4: number;
// Light5: number;
// }
2. 100 个温度传感器
TypeScript
ts
type Sensors = CreateIotData<'T', number, 100>;
// T1 ~ T100
3. 自动补全 & 类型检查
在 VS Code 中键入:
TypeScript
ts
const data: Bulbs = {
Light1: 80,
Light2: 90,
// ❌ 直接提示缺少 Light3/4/5
}
🧪 更多玩法
表格
需求示例 | 用法 |
---|---|
十六进制地址 | CreateIotData<'0x', string, 16> → 0x1 ~ 0x10 |
布尔开关 | CreateIotData<'SW', boolean, 8> |
动态前缀 | CreateIotData< device_${string}, number, 50> |
📌 总结
- ✅ 零运行时开销:全部是编译期类型
- ✅ 1 开始编号 :告别
device0
- ✅ 可维护 :改
Count
即可重新生成
把这段类型丢进你的 IoT 工具库,下次评审再也不用解释"为什么从 0 开始"了!
📌 整体代码
ts
type BuildTuple<L extends number, T extends any[] = []> =
T['length'] extends L
? T
: BuildTuple<L, [...T, any]>;
type TupleIndices<T extends readonly any[]> = keyof T & `${number}`;
type AddOne<S extends string> = `${S}` extends `${infer N extends number}`
? [...BuildTuple<N>, any]['length']
: never;
type CreateIotData<
Prefix extends string,
ValueType,
Count extends number
> = {
[K in TupleIndices<BuildTuple<Count>> as `${Prefix}${AddOne<K>}`]: ValueType;
};