在 TypeScript 中,你可以使用泛型来实现基于动态值返回对应类型的功能。对于你给出的示例,你可以对 test
函数使用泛型,并在函数内部根据 Params
对象的 type
属性返回对应的类型。
下面是一个可能的实现方式:
type Params = {
type: 'human' | 'animal',
id: string | number
}
class A {
name: string;
constructor(val: string) {
this.name = val;
}
}
class B {
age: number;
constructor(val: number) {
this.age = val;
}
}
function test<T extends Params>(val: T): T['type'] extends 'human' ? A : T['type'] extends 'animal' ? B : never {
switch (val.type) {
case 'human':
return new A(val.id as any); // 这里使用 any 断言是因为 val.id 的类型可能是 string | number,而 A 的构造函数参数需要是 string
case 'animal':
return new B(val.id as any); // 同上
default:
throw new Error(`Unexpected type: ${val.type}`);
}
}
const result = test({ type: 'human', id: 'JohnDoe' }); // result 的类型会被推导为 A
result.name; // 可以访问 A 类的 name 属性
const animalResult = test({ type: 'animal', id: 42 }); // animalResult 的类型会被推导为 B
animalResult.age; // 可以访问 B 类的 age 属性
在上面的代码中,我们使用了条件类型(Conditional Types)来根据 Params
对象的 type
属性返回对应的类型。当 type
是 'human'
时,函数返回 A
类型;当 type
是 'animal'
时,函数返回 B
类型。如果 type
是其他值,函数会抛出一个错误。
注意,在调用 new A(val.id as any)
和 new B(val.id as any)
时,我们使用了 as any
断言。这是因为 val.id
的类型可能是 string | number
,而 A
的构造函数参数需要是 string
,B
的构造函数参数需要是 number
。使用 as any
断言可以绕过类型检查,但需要注意确保传递的参数是正确的类型,以避免运行时错误。
此外,你还可以考虑使用工厂函数或工厂类来创建对象,这样可以根据传入的参数动态地创建和返回正确的类型。这样可以避免在 test
函数中使用 as any
断言,并使代码更加清晰和易于维护。
一个粗糙的方案: