假设我们有一个帮助函数来辅助我们创建一个对象。那么如何才能让这个函数最后返回的值是一个 键值完整 的对象呢?
一、举例说明
上代码
//目标对象:
type TargetType = {
name: string,
age: number,
salary: number
}
// 示例代码
const data: Record<string, any> = {}
const builder = {
add(key: string, value: any) {
data[key] = value
return builder
},
build() {
return data
}
}
const result = builder.add('name', 'haoza').add('age', 30).add('salary', 2233).build()
type Type = typeof result
// 请问 Type 的类型是什么呢?
// 当然毫无疑问是一个 Record<string, any>
上述代码最终
Type 肯定是 Record<string, any>,毕竟 data 的类型就是这个
那么如何才能得到我们期望的类型呢?
二、借助体操函数
type Builder<
T extends Record<string, any> = Record<string, never>,
> = {
add: <N extends string, I extends any>(
name: N,
item: I,
) => Builder<T & { [k in N]: I }>;
build: () => T;
};
/**
* 完备的类型校验
*/
export const BuilderCreator = (): Builder => {
const items: Record<string, any> = {};
const builder = {
add: (name: string, val: any) => {
items[name] = val;
return builder;
},
build: () => {
// 类型推到已经由体操函数支持,所以使用any不影响类型推断
return items as any;
},
};
return builder;
};
const builder = BuilderCreator()
const result = builder.add('name', 'haoza').add('age', 30).add('salary', 2233).build()
非常完美!不过有两个个关键点可以注意下
Builder 的范型参数默认值用的是 Record<string, never>
BuilderCreator 是一个普通函数而不是类class,因为使用类的实例化可能会导致嵌套层级过深而报错
三、检查key是否重复
一般来说重复传入key很容易导致bug,那么我们该如何限制呢?关键就在于如何判断 key 已经存在和抛出错误
type Builder<
T extends Record<string, any> = Record<string, never>,、
// 增加了新的参数
F extends string = '',
> = {
add: <N extends string, I extends any>(
// 判断重复则产生 never
name: N & (N extends F ? '重复的属性,请注意修改' : N),
item: I,
) => Builder<T & { [k in N]: I }, F | N>;
build: () => T;
};
/**
* 完备的类型校验
*/
export const BuilderCreator = (): Builder => {
const items: Record<string, any> = {};
const builder = {
add: (name: string, val: any) => {
items[name] = val;
return builder;
},
build: () => {
// 类型推到已经由体操函数支持,所以使用any不影响类型推断
return items as any;
},
};
return builder;
};
const builder = BuilderCreator()
const result = builder.add('name', 'haoza').add('name', 'hao')
最后再把 add 函数中 val 的类型改成项目中真实的约束,快去装X了~