convertMembers = (jsDoc = '', type, src, parentName = null) => {
  // type could be an array of types like: `{sth: 1} | string` - so we parse
  // each type separately
  const typesToCheck = [type]
  if (type.types && type.types.length) {
    typesToCheck.push(...type.types)
  }
  typesToCheck.forEach(type => {
    // Handling array defined like this: {element1: 'something'}[]
    if(ts.isArrayTypeNode(type) && type.elementType) {
      jsDoc = convertMembers(jsDoc, type.elementType, src, parentName ? parentName + '[]' : '[]')
    }

    // Handling Array<{element1: 'something'}>
    if (type.typeName && type.typeName.escapedText === 'Array') {
      if(type.typeArguments && type.typeArguments.length) {
        type.typeArguments.forEach(subType => {

          jsDoc = convertMembers(jsDoc, subType, src, parentName
            ? parentName + '[]'
            : '' // when there is no parent - jsdoc cannot parse [].name
          )
        })
      }
    }
    // Handling {property1: "value"}
    (type.members || []).filter(m => ts.isTypeElement(m)).forEach(member => {
      let name = getName(member, src)
      let comment = getCommentAsString(member, src)
      const members = member.type.members || []
      let typeName = members.length ? 'object' : getTypeName(member.type, src)
      if (parentName) {
        name = [parentName, name].join('.')
      }
      // optional
      const nameToPlace = member.questionToken ? `[${name}]` : name
      jsDoc = appendComment(jsDoc, `@property {${typeName}} ${nameToPlace}   ${comment}`)
      jsDoc = convertMembers(jsDoc, member.type, src, name)
    })
  })
  return jsDoc
}
Convert type properties to @property
@param {string} [jsDoc]  existing jsdoc text where all @param comments should be appended
@param {ts.TypeNode} wrapper ts node which has to be parsed
@param {string} src      source for an entire parsed file (we are fetching substrings from it)
@param {string} parentName     name of a parent element
@returns {string} modified jsDoc comment with appended @param tags