class Geet::Github::AbstractIssue

other methods could be moved in here at some complexity cost.
For clarity, in this class we keep only the identical logic between the subclasses, but

def self.list(api_interface, milestone: nil, assignee: nil, &type_filter)

def self.list(api_interface, milestone: nil, assignee: nil, &type_filter)
  api_path = 'issues'
  request_params = {}
  request_params[:milestone] = milestone.number if milestone
  request_params[:assignee] = assignee.username if assignee
  response = T.cast(api_interface.send_request(api_path, params: request_params, multipage: true), T::Array[T::Hash[String, T.untyped]])
  abstract_issues_list = response.map do |issue_data|
    number = T.cast(issue_data.fetch('number'), Integer)
    title = T.cast(issue_data.fetch('title'), String)
    link = T.cast(issue_data.fetch('html_url'), String)
    new(number, api_interface, title, link) if type_filter.nil? || type_filter.call(issue_data)
  end
  abstract_issues_list.compact
end

def add_labels(labels)

def add_labels(labels)
  api_path = "issues/#{@number}/labels"
  @api_interface.send_request(api_path, data: labels)
end

def assign_users(users)

def assign_users(users)
  api_path = "issues/#{@number}/assignees"
  request_data = {assignees: Array(users)}
  @api_interface.send_request(api_path, data: request_data)
end

def comment(comment)

def comment(comment)
  api_path = "issues/#{@number}/comments"
  request_data = {body: comment}
  @api_interface.send_request(api_path, data: request_data)
end

def edit(milestone:)

def edit(milestone:)
  request_data = {milestone: milestone}
  api_path = "issues/#{@number}"
  @api_interface.send_request(api_path, data: request_data, http_method: :patch)
end

def initialize(number, api_interface, title, link)

def initialize(number, api_interface, title, link)
  @number = number
  @api_interface = api_interface
  @title = title
  @link = link
end