映射打字稿类型
Mapping typescript types
我正在尝试向函数添加类型,该函数将类型对象数组作为参数,return 另一种类型的映射数组:
const createAnimals = <T extends AnimalFactory<any>[]>(factories: T) => {
return factories.map((f) => f());
};
一旦这个函数被正确输入,它应该会抛出一个错误,代码如下:
const factories = [() => new Dog(), () => new Cat()];
const animals = createAnimals(factories);
// ❌ should fail !
animals[0].meow();
我希望编译器知道 animals
是 [Dog, Cat]
类型。
我一直在尝试使用 infer
,但到目前为止没有成功。
可以这样做,但解决方案将要求您将 factories
键入为 read-only,否则该类型将根本不包含足够的信息。
const factories = [() => new Dog(), () => new Cat()] as const;
如果你为这样的工厂声明一个类型(这不再是真正关于动物的,但让我们坚持下去)
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
的 return 类型:
const createAnimals = <T extends AnimalFactory<unknown>[]>
(factories: readonly [...T]) => factories.map(f => f()) as FactoryAnimals<T>;
不幸的是,as FactoryAnimals<T>
断言是必要的,因为 TypeScript 无法推断在元组上进行映射会产生映射类型。 readonly [...T]
位是为了避免 read-only T
,这会导致 read-only return 类型并需要额外的断言。
如果您在工厂上调用 createAnimals
,它会生成所需类型的元组:
const animals = createAnimals(factories);
// type: [Dog, Cat]
// ❌ should fail !
animals[0].meow(); // Error: Property 'meow' does not exist on type 'Dog'.
如果你用显式数组而不是变量调用它,你可以省略 as const
,因为 [...T]
类型导致 factories
被认为是一个元组:
const animals = createAnimals([() => new Dog(), () => new Cat()]);
// type: [Dog, Cat]
我正在尝试向函数添加类型,该函数将类型对象数组作为参数,return 另一种类型的映射数组:
const createAnimals = <T extends AnimalFactory<any>[]>(factories: T) => {
return factories.map((f) => f());
};
一旦这个函数被正确输入,它应该会抛出一个错误,代码如下:
const factories = [() => new Dog(), () => new Cat()];
const animals = createAnimals(factories);
// ❌ should fail !
animals[0].meow();
我希望编译器知道 animals
是 [Dog, Cat]
类型。
我一直在尝试使用 infer
,但到目前为止没有成功。
可以这样做,但解决方案将要求您将 factories
键入为 read-only,否则该类型将根本不包含足够的信息。
const factories = [() => new Dog(), () => new Cat()] as const;
如果你为这样的工厂声明一个类型(这不再是真正关于动物的,但让我们坚持下去)
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
的 return 类型:
const createAnimals = <T extends AnimalFactory<unknown>[]>
(factories: readonly [...T]) => factories.map(f => f()) as FactoryAnimals<T>;
不幸的是,as FactoryAnimals<T>
断言是必要的,因为 TypeScript 无法推断在元组上进行映射会产生映射类型。 readonly [...T]
位是为了避免 read-only T
,这会导致 read-only return 类型并需要额外的断言。
如果您在工厂上调用 createAnimals
,它会生成所需类型的元组:
const animals = createAnimals(factories);
// type: [Dog, Cat]
// ❌ should fail !
animals[0].meow(); // Error: Property 'meow' does not exist on type 'Dog'.
如果你用显式数组而不是变量调用它,你可以省略 as const
,因为 [...T]
类型导致 factories
被认为是一个元组:
const animals = createAnimals([() => new Dog(), () => new Cat()]);
// type: [Dog, Cat]