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?
CodePudding user response:
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.
CodePudding user response:
The createMany
doesn't even need to know the type factory
returns.
You can make it generic for more flexibility.
interface ChannelItem { foo: string; }
function channelFactory(): ChannelItem {
return { foo: "bar" }
}
function createMany<T>(factory: () => T, count = 1): T[] {
// A for loop that calls the factory, pushes those into an array and returns that array
return []
}
function saveChannels(payload: ChannelItem[]): void { }
const items = createMany(channelFactory, 5);
saveChannels(items)