0

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 }

1 Answer 1

1

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)
Sign up to request clarification or add additional context in comments.

1 Comment

Hey thanks, this was very helpful. I've tried o look the conditionally typing from the docs before posting but I've found the extends syntax quite difficult to understand. But your response was clear to me. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.