这样做是可能的,但解决方案将要求您以只读方式键入factories
,否则该类型将根本不包含足够的信息.
const factories = [() => new Dog(), () => new Cat()] as const;
如果你为这样的工厂声明了一个类型(这已经不是关于动物的了,但让我们坚持下go )
type AnimalFactory<T> = () => T
您可以创建一个映射类型,该类型采用AnimalFactory
的元组,并生成相应动物类型的元组:
type FactoryAnimals<T extends AnimalFactory<unknown>[]> =
{[K in keyof T]: T[K] extends AnimalFactory<infer A> ? A : never}
现在,您可以使用AnimalFactory
作为createAnimals
的返回类型:
const createAnimals = <T extends AnimalFactory<unknown>[]>
(factories: readonly [...T]) => factories.map(f => f()) as FactoryAnimals<T>;
不幸的是,as FactoryAnimals<T>
断言是必需的,因为TypeScript无法推断对元组进行映射会产生映射类型.readonly [...T]
位是为了避免只读T
,这将导致只读返回类型,并需要额外的断言.
如果在工厂中调用createAnimals
,它将生成所需类型的元组:
const animals = createAnimals(factories);
// type: [Dog, Cat]
// ❌ should fail !
animals[0].meow(); // Error: Property 'meow' does not exist on type 'Dog'.
如果用显式数组而不是变量来调用它,可以省go as const
,因为[...T]
类型导致factories
被视为元组:
const animals = createAnimals([() => new Dog(), () => new Cat()]);
// type: [Dog, Cat]
TypeScript playground