private function decodeValue($value, array $type)
    {
        if ($value === null || $value === '') {
            return $value;
        }

        switch ($type['code']) {
            case self::TYPE_INT64:
                $value = $this->returnInt64AsObject
                    ? new Int64($value)
                    : (int) $value;
                break;

            case self::TYPE_TIMESTAMP:
                $value = $this->parseTimeString($value);
                $value = new Timestamp($value[0], $value[1]);
                break;

            case self::TYPE_DATE:
                $value = new Date(new \DateTimeImmutable($value));
                break;

            case self::TYPE_BYTES:
                $value = new Bytes(base64_decode($value));
                break;

            case self::TYPE_ARRAY:
                $res = [];
                foreach ($value as $item) {
                    $res[] = $this->decodeValue($item, $type['arrayElementType']);
                }

                $value = $res;
                break;

            case self::TYPE_STRUCT:
                $fields = isset($type['structType']['fields'])
                    ? $type['structType']['fields']
                    : [];

                $value = $this->decodeValues($fields, $value, Result::RETURN_ASSOCIATIVE);
                break;

            case self::TYPE_FLOAT64:
                // NaN, Infinite and -Infinite are possible FLOAT64 values,
                // but when the gRPC response is decoded, they are represented
                // as strings. This conditional checks for a string, converts to
                // an equivalent double value, or dies if something really weird
                // happens.
                if (is_string($value)) {
                    switch ($value) {
                        case 'NaN':
                            $value = NAN;
                            break;

                        case 'Infinity':
                            $value = INF;
                            break;

                        case '-Infinity':
                            $value = -INF;
                            break;

                        default:
                            throw new \RuntimeException(sprintf(
                                'Unexpected string value %s encountered in FLOAT64 field.',
                                $value
                            ));
                    }
                }

                break;
        }

        return $value;
    }