I’m learning TypeScript, and decided to try implement it in a small portion of my codebase to get the ball rolling. Specifically, what I’m refactoring now is related to a fixture “factory” for the purpose of generating fixtures for Jest tests.
In addition to these factories, which spit out certain Objects, I also have some helper methods that make things like generating multiple objects a bit easier.
A factory is fairly simple, it looks something like this (the values are spoofed with faker.js):
function channelFactory(): ChannelItem { return { foo: "bar" } }
A ChannelItem is just a simple Object containing some keys
interface ChannelItem { foo: string; }
And as an example of one of those helper methods, I have a createMany
function that takes in a Factory function and a Count as arguments
function createMany(factory: () => Record<string, unknown>, count = 1): Record<string, any>[] { // A for loop that calls the factory, pushes those into an array and returns that array }
However, if I try to use these factories somewhere, for example in this function that persists some created channels into the DB, I get the TS compiler warning me about Record<string, any>[]
not being assignable to ChannelItem[]
.
function saveChannels(payload: ChannelItem[]): void { // Unimportant implementation details }
const items = createMany(channelFactory, 5); saveChannels(items) // => Argument type Record<string, any>[] is not assignable to parameter type ChannelItem[] Type Record<string, any> is not assignable to type ChannelItem
I know this is a commonly known issue with Interfaces specifically (Issue #15300) and that the potential solution would be to declare a type
rather than an interface
, however in this situation I still get the same warning.
type ChannelItem = { foo: string } // Still gives me the above warning
What would the ideal way of making my factory
functions more generic here be?
Advertisement
Answer
You could make the createMany function generic:
function createMany<K extends string, T>(factory: () => Record<K, T>, count = 1): Record<K, T>[] { const arr = []; for (let i = 0; i < count; i++) { arr.push(factory()); } return arr; } const items = createMany(channelFactory, 5); console.log(items); // Prints: //[ // { foo: 'bar' }, // { foo: 'bar' }, // { foo: 'bar' }, // { foo: 'bar' }, // { foo: 'bar' } //]
I made K extends string
because you specified you want your record to have string keys. T can be anything you want.
Just have to fill in the functions yourself, not sure what you want done in those.