Search code examples
typescriptfactory-pattern

How to set type in Factory pattern in which exists multiple interfaces


There are like these types

interface A {
  id: number
  name: string
  patternA: patternA[]
}

interface B {
  id: number
  name: string
  patternB: patternB[]
}

interface C {
  id: number
  name: string
  patternC: patternC[]
  city: string
}

and there is factory method

const factory = (type, data: /** 🤔 what should I write?? **/) => {
  switch (type) {
    case 'first':
      return createHogeA(data)
    case 'second':
      return createHogeB(data)
    case 'third':
      return createHogeC(data)
    default:
      return
  }
}

const createHogeA = (data: A) => somefunctionA(data)
const createHogeB = (data: B) => somefunctionB(data)
const createHogeC = (data: C) => somefunctionC(data)

What I've tried so far

  1. Set type like this, but then appears error because it's possible that each datas type could pass to each functions
const factory = (type, data: A | B | C) => {
  switch (type) {
    case 'first':
      return createHogeA(data)
    case 'second':
      return createHogeB(data)
    case 'third':
      return createHogeC(data)
    default:
      return
  }
}
  1. Create base interface
interface base {
  id: number
  name: string
}

But then honestly I didn't have idea


Solution

  • The easiest way to handle this is to use the as keyword to tell the compiler which of the interfaces the data object satisfies when calling either of the functions.

    const factory = (type: string, data: A | B | C) => {
      switch (type) {
        case 'first':
          return createHogeA(data as A)
        case 'second':
          return createHogeB(data as B)
        case 'third':
          return createHogeC(data as C)
        default:
          return
      }
    }
    

    You could also use guards to determine which interface data satisfies

    const isA = (data: any): data is A => {
      return (data as A).patternA !== undefined;
    }
    
    const isB = (data: any): data is B => {
      return (data as B).patternB !== undefined;
    }
    
    const isC = (data: any): data is C => {
      return (data as C).patternC !== undefined && (data as C).city !== undefined;
    }
    
    const factory = (_type: string, data: A | B | C) => {
      if(isA(data)) {
        createHogeA(data)
      }
      // Rest of code here
    }