function readElement(reader, type) {
  switch (type) {
  case CompactType.TRUE:
    return true
  case CompactType.FALSE:
    return false
  case CompactType.BYTE:
    // read byte directly
    return reader.view.getInt8(reader.offset++)
  case CompactType.I16:
  case CompactType.I32:
    return readZigZag(reader)
  case CompactType.I64:
    return readZigZagBigInt(reader)
  case CompactType.DOUBLE: {
    const value = reader.view.getFloat64(reader.offset, true)
    reader.offset += 8
    return value
  }
  case CompactType.BINARY: {
    const stringLength = readVarInt(reader)
    const strBytes = new Uint8Array(reader.view.buffer, reader.view.byteOffset + reader.offset, stringLength)
    reader.offset += stringLength
    return strBytes
  }
  case CompactType.LIST: {
    const [elemType, listSize] = readCollectionBegin(reader)
    const boolType = elemType === CompactType.TRUE || elemType === CompactType.FALSE
    const values = new Array(listSize)
    for (let i = 0; i < listSize; i++) {
      values[i] = boolType ? readElement(reader, CompactType.BYTE) === 1 : readElement(reader, elemType)
    }
    return values
  }
  case CompactType.STRUCT: {
    /** @type {ThriftObject} */
    const structValues = {}
    let structLastFid = 0
    while (true) {
      let structFieldType, structFid
      [structFieldType, structFid, structLastFid] = readFieldBegin(reader, structLastFid)
      if (structFieldType === CompactType.STOP) {
        break
      }
      structValues[`field_${structFid}`] = readElement(reader, structFieldType)
    }
    return structValues
  }
  // TODO: MAP, SET, UUID
  default:
    throw new Error(`thrift unhandled type: ${type}`)
  }
}
Read a single element based on its type

@import {DataReader, ThriftObject, ThriftType} from '../src/types.d.ts'
@param {DataReader} reader
@param {number} type
@returns {ThriftType}