function getParameters(inferFnType) {

    // work around define functions before use warning.
    var recordTypeToString, inferTypeToString, processInferFnTypeParameters, inferFnTypeToString;

    /**
     *  Convert an infer array type to a string.
     *
     *  Formatted using google closure style. For example:
     *
     *  "Array.<string, number>"
     *
     * @param {Infer.Arr} inferArrType
     *
     * @return {string} - array formatted in google closure style.
     *
     */
    function inferArrTypeToString(inferArrType) {
        var result = "Array.<";

        result += inferArrType.props["<i>"].types.map(inferTypeToString).join(", ");

        // workaround case where types is zero length
        if (inferArrType.props["<i>"].types.length === 0) {
            result += "Object";
        }
        result += ">";

        return result;
    }

    /**
     * Convert properties to a record type annotation.
     *
     * @param {Object} props
     * @return {string} - record type annotation
     */
    recordTypeToString = function (props) {
        var result = "{",
            first = true,
            prop;

        result += Object.keys(props).map(function (key) {
            return key + ": " + inferTypeToString(props[key]);
        }).join(", ");

        result += "}";

        return result;
    };

    /**
     *  Convert an infer type to a string.
     *
     * @param {*} inferType - one of the Infer's types; Infer.Prim, Infer.Arr, Infer.ANull. Infer.Fn functions are
     * not handled here.
     *
     * @return {string}
     *
     */
    inferTypeToString = function (inferType) {
        var result;

        if (inferType instanceof Infer.AVal) {
            inferType = inferType.types[0];
        }

        if (inferType instanceof Infer.Prim) {
            result = inferType.toString();
            if (result === "string") {
                result = "String";
            } else if (result === "number") {
                result = "Number";
            } else if (result === "boolean") {
                result = "Boolean";
            }
        } else if (inferType instanceof Infer.Arr) {
            result = inferArrTypeToString(inferType);
        } else if (inferType instanceof Infer.Fn) {
            result = inferFnTypeToString(inferType);
        } else if (inferType instanceof Infer.Obj) {
            if (inferType.name === undefined) {
                result = recordTypeToString(inferType.props);
            } else {
                result = inferType.name;
            }
        } else {
            result = "Object";
        }

        return result;
    };

    /**
     * Format the given parameter array. Handles separators between
     * parameters, syntax for optional parameters, and the order of the
     * parameter type and parameter name.
     *
     * @param {!Array.<{name: string, type: string, isOptional: boolean}>} params -
     * array of parameter descriptors
     * @param {function(string)=} appendSeparators - callback function to append separators.
     * The separator is passed to the callback.
     * @param {function(string, number)=} appendParameter - callback function to append parameter.
     * The formatted parameter type and name is passed to the callback along with the
     * current index of the parameter.
     * @param {boolean=} typesOnly - only show parameter types. The
     * default behavior is to include both parameter names and types.
     * @return {string} - formatted parameter hint
     */
    function formatParameterHint(params, appendSeparators, appendParameter, typesOnly) {
        var result = "",
            pendingOptional = false;

        params.forEach(function (value, i) {
            var param = value.type,
                separators = "";

            if (value.isOptional) {
                // if an optional param is following by an optional parameter, then
                // terminate the bracket. Otherwise enclose a required parameter
                // in the same bracket.
                if (pendingOptional) {
                    separators += "]";
                }

                pendingOptional = true;
            }

            if (i > 0) {
                separators += ", ";
            }

            if (value.isOptional) {
                separators += "[";
            }

            if (appendSeparators) {
                appendSeparators(separators);
            }

            result += separators;

            if (!typesOnly) {
                param += " " + value.name;
            }

            if (appendParameter) {
                appendParameter(param, i);
            }

            result += param;

        });

        if (pendingOptional) {
            if (appendSeparators) {
                appendSeparators("]");
            }

            result += "]";
        }

        return result;
    }

    /**
     * Convert an infer function type to a Google closure type string.
     *
     * @param {Infer.Fn} inferType - type to convert.
     * @return {string} - function type as a string.
     */
    inferFnTypeToString = function (inferType) {
        var result = "function(",
            params = processInferFnTypeParameters(inferType);

        result += /*HintUtils2.*/formatParameterHint(params, null, null, true);
        if (inferType.retval) {
            result += "):";
            result += inferTypeToString(inferType.retval);
        }

        return result;
    };

    /**
     * Convert an infer function type to string.
     *
     * @param {*} inferType - one of the Infer's types; Infer.Fn, Infer.Prim, Infer.Arr, Infer.ANull
     * @return {Array<{name: string, type: string, isOptional: boolean}>} where each entry in the array is a parameter.
     */
    processInferFnTypeParameters = function (inferType) {
        var params = [],
            i;

        for (i = 0; i < inferType.args.length; i++) {
            var param = {},
                name = inferType.argNames[i],
                type = inferType.args[i];

            if (!name) {
                name = "param" + (i + 1);
            }

            if (name[name.length - 1] === "?") {
                name = name.substring(0, name.length - 1);
                param.isOptional = true;
            }

            param.name = name;
            param.type = inferTypeToString(type);
            params.push(param);
        }

        return params;
    };

    return processInferFnTypeParameters(inferFnType);
}
Given a Tern type object, convert it to an array of Objects, where each object describes
 a parameter.

@param {!Infer.Fn} inferFnType - type to convert.
@return {Array<{name: string, type: string, isOptional: boolean}>} where each entry in the array is a parameter.