使用TypeScript创建类型安全的Fake对象
问题:基于TypeScript类型自动生成Fake实例是否需要Babel预处理?
需求场景
假设定义如下TypeScript类型:
type Foo = { id: string, foo: () => string }
希望创建一个Fake实例,自动为类型中的属性生成符合类型的默认值:
const foo = new Fake<Foo>() foo.id // 返回空字符串 "" foo.foo() // 返回空字符串 ""
这种能力在测试中模拟第三方依赖时非常实用,核心难点是仅通过TypeScript类型信息生成有效的JavaScript代码。
当前非类型安全的Fake实现
以下是一个初步实现,但缺乏类型安全性:
export class Fake { constructor({ props = {}, didCallMethod = () => {}, }: { props?: Record<string, any> didCallMethod?: (name: string, args: any[]) => void } = {}) { return new Proxy<any>({}, { get: (target, prop) => { if (prop in props) { // @ts-expect-error return props[prop] } if (prop in target) { return target[prop] } return (...args: any[]) => { return didCallMethod(String(prop), args) } }, }) } }
解答:不需要Babel预处理
TypeScript的类型信息仅存在于编译阶段,最终会被擦除为纯JavaScript代码。要实现类型安全的Fake,核心是利用TypeScript的泛型、条件类型、索引类型等特性约束代码行为,完全不需要依赖Babel这类语法转换工具。
类型安全的Fake实现示例
可以通过泛型约束和类型推导,让Fake类严格遵循传入的类型定义:
// 推导属性的返回类型:如果是函数则取其返回值,否则取属性本身类型 type FakeReturnType<T> = T extends () => infer R ? R : T; export class Fake<T> { constructor({ // 约束props必须符合T的部分属性类型 props = {} as Partial<{ [K in keyof T]: FakeReturnType<T[K]> }>, // 约束回调函数的参数和返回值类型 didCallMethod = () => "" as FakeReturnType<T[keyof T]>, }: { props?: Partial<{ [K in keyof T]: FakeReturnType<T[K]> }>; didCallMethod?: (name: keyof T, args: any[]) => FakeReturnType<T[keyof T]>; } = {}) { return new Proxy({} as T, { get: (target, prop: keyof T) => { // 优先返回自定义props中的值 if (prop in props) { const value = props[prop]; // 如果是函数,包装后调用didCallMethod if (typeof value === 'function') { return (...args: any[]) => { didCallMethod(prop, args); return value(...args); }; } return value; } // 返回目标对象本身已有的属性 if (prop in target) { return (target as any)[prop]; } // 根据类型生成默认值 const propType = typeof ({} as T)[prop]; if (propType === 'function') { return (...args: any[]) => didCallMethod(prop, args); } else { // 针对不同基础类型返回对应默认值 switch (propType) { case 'string': return ""; case 'number': return 0; case 'boolean': return false; default: return null; } } }, }); } }
实现说明
- 用泛型
T约束整个Fake类的属性结构,确保访问的属性都属于T的键集合。 - 通过
Partial<{ [K in keyof T]: ... }>约束props参数的类型,避免传入不符合T的属性。 - 利用条件类型
FakeReturnType区分函数和非函数属性,确保回调函数的返回值符合类型要求。 - 最终编译后的JavaScript代码是纯ES Proxy实现,不需要任何Babel插件或预处理步骤。
Babel的核心作用是语法降级(如将ES6+代码转换为ES5兼容代码),而这里的需求是利用TypeScript的类型系统实现类型安全的模拟,和Babel没有关联,完全不需要额外的Babel预处理。
内容的提问来源于stack exchange,提问作者david_adler
相关产品推荐
相关产品推荐

