Reputation: 277
i'm trying to make a factory for my models in Typescript using the faker package. I've manage to create a functional generic factory, like the casual package api, that receives a generic model maker, and a options to override the created model. That generic factory generates a model factory, so its a factory that generates a factory. This generated factory can receive two parameters, the first one is the amount of model that i want to make, the default value is 1 and the second parameter is the options that i want to overwrite on my models. The problem that i have is, i don't know if is possible to conditionally determinate the return type of the factory based on the quantity value automatically, such as if the quantity is one, i should return IModel
but if the quantity is bigger than one i should return IModel[]
.
Right now I've explicit returning IModel | IModel[]
, and whenever i use the factories i must type the return like this:
jest.spyOn(registerUserStub, 'execute').mockResolvedValueOnce(userFactory(1) as IUserModel)
My code:
// My User Model
export type IUserModel = {
id: string,
name: string,
email: string,
password: string,
active: boolean,
confirmed: boolean
}
Factory Maker
import { DeepPartial } from 'utility-types'
export function factoryMaker<T = any> (objMaker: (options?: DeepPartial<T>) => T): (quantity: number, options?: DeepPartial<T>) => T | T[] {
return (quantity, options) => {
const entitiesArray = new Array(quantity).fill(null).map(() => objMaker(options))
return quantity === 1 ? entitiesArray[0] : entitiesArray
}
}
My User Factory
import { DeepPartial } from 'utility-types'
import faker from 'faker'
import { IUserModel } from '../models'
import { factoryMaker } from './factoryMaker'
type OptionsType = DeepPartial<IUserModel>
function makeUser (options?: OptionsType):IUserModel {
return {
id: faker.random.uuid(),
password: faker.random.uuid(),
email: faker.internet.email(),
name: faker.name.findName(),
confirmed: options.confirmed !== undefined ? options.confirmed : true,
active: true,
...options
}
}
const userFactory = factoryMaker<IUserModel>(makeUser)
export { userFactory }
Upvotes: 0
Views: 68
Reputation: 14148
You could make factoryMaker
return N extends 1 ? T : T[]
, where N
is the quantity:
export function factoryMaker<T = any>(
objMaker: (options?: DeepPartial<T>) => T
): <N extends number>(
quantity: N,
options?: DeepPartial<T>
) => N extends 1 ? T : T[] {
return <N extends number>(
quantity: N,
options?: DeepPartial<T>
): N extends 1 ? T : T[] => {
const entitiesArray = new Array(quantity).fill(null).map(() => objMaker(options))
return (quantity === 1 ? entitiesArray[0] : entitiesArray) as N extends 1 ? T : T[]
}
}
// ...
// IUserModel
const oneUser = userFactory(1)
// IUserModel[]
const twoUsers = userFactory(2)
// IUserModel | IUserModel[]
const oneOrTwoUsers = userFactory(Math.random() > 0.5 ? 1 : 2)
Upvotes: 1