var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
    function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
    var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
    var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
    var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
    var _, done = false;
    for (var i = decorators.length - 1; i >= 0; i--) {
        var context = {};
        for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
        for (var p in contextIn.access) context.access[p] = contextIn.access[p];
        context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
        var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
        if (kind === "accessor") {
            if (result === void 0) continue;
            if (result === null || typeof result !== "object") throw new TypeError("Object expected");
            if (_ = accept(result.get)) descriptor.get = _;
            if (_ = accept(result.set)) descriptor.set = _;
            if (_ = accept(result.init)) initializers.unshift(_);
        }
        else if (_ = accept(result)) {
            if (kind === "field") initializers.unshift(_);
            else descriptor[key] = _;
        }
    }
    if (target) Object.defineProperty(target, contextIn.name, descriptor);
    done = true;
};
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
    var useValue = arguments.length > 2;
    for (var i = 0; i < initializers.length; i++) {
        value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
    }
    return useValue ? value : void 0;
};
import { ReflectionSymbolId } from "./ReflectionSymbolId.js";
import { ReflectionKind } from "./kind.js";
import { Comment } from "./Comment.js";
import { i18n, joinArray, NonEnumerable } from "#utils";
/**
 * Base class of all type definitions.
 * @category Types
 */
export class Type {
    /**
     * Return a string representation of this type.
     */
    toString() {
        return this.stringify(TypeContext.none);
    }
    visit(visitor, ...args) {
        return visitor[this.type]?.(this, ...args);
    }
    stringify(context) {
        if (this.needsParenthesis(context)) {
            return `(${this.getTypeString()})`;
        }
        return this.getTypeString();
    }
    // Nothing to do for the majority of types.
    fromObject(_de, _obj) { }
    /**
     * Return the estimated size of the type if it was all printed on one line.
     */
    estimatePrintWidth() {
        return this.getTypeString().length;
    }
}
export function makeRecursiveVisitor(visitor) {
    const recursiveVisitor = {
        namedTupleMember(type) {
            visitor.namedTupleMember?.(type);
            type.element.visit(recursiveVisitor);
        },
        templateLiteral(type) {
            visitor.templateLiteral?.(type);
            for (const [h] of type.tail) {
                h.visit(recursiveVisitor);
            }
        },
        array(type) {
            visitor.array?.(type);
            type.elementType.visit(recursiveVisitor);
        },
        conditional(type) {
            visitor.conditional?.(type);
            type.checkType.visit(recursiveVisitor);
            type.extendsType.visit(recursiveVisitor);
            type.trueType.visit(recursiveVisitor);
            type.falseType.visit(recursiveVisitor);
        },
        indexedAccess(type) {
            visitor.indexedAccess?.(type);
            type.indexType.visit(recursiveVisitor);
            type.objectType.visit(recursiveVisitor);
        },
        inferred(type) {
            visitor.inferred?.(type);
            type.constraint?.visit(recursiveVisitor);
        },
        intersection(type) {
            visitor.intersection?.(type);
            type.types.forEach((t) => t.visit(recursiveVisitor));
        },
        intrinsic(type) {
            visitor.intrinsic?.(type);
        },
        literal(type) {
            visitor.literal?.(type);
        },
        mapped(type) {
            visitor.mapped?.(type);
            type.nameType?.visit(recursiveVisitor);
            type.parameterType.visit(recursiveVisitor);
            type.templateType.visit(recursiveVisitor);
        },
        optional(type) {
            visitor.optional?.(type);
            type.elementType.visit(recursiveVisitor);
        },
        predicate(type) {
            visitor.predicate?.(type);
            type.targetType?.visit(recursiveVisitor);
        },
        query(type) {
            visitor.query?.(type);
            type.queryType.visit(recursiveVisitor);
        },
        reference(type) {
            visitor.reference?.(type);
            type.typeArguments?.forEach((t) => t.visit(recursiveVisitor));
        },
        reflection(type) {
            visitor.reflection?.(type);
            // Future: This should maybe recurse too?
            // See the validator in exports.ts for how to do it.
        },
        rest(type) {
            visitor.rest?.(type);
            type.elementType.visit(recursiveVisitor);
        },
        tuple(type) {
            visitor.tuple?.(type);
            type.elements.forEach((t) => t.visit(recursiveVisitor));
        },
        typeOperator(type) {
            visitor.typeOperator?.(type);
            type.target.visit(recursiveVisitor);
        },
        union(type) {
            visitor.union?.(type);
            type.types.forEach((t) => t.visit(recursiveVisitor));
        },
        unknown(type) {
            visitor.unknown?.(type);
        },
    };
    return recursiveVisitor;
}
/**
 * Enumeration that can be used when traversing types to track the location of recursion.
 * Used by TypeDoc internally to track when to output parenthesis when rendering.
 * @enum
 */
export const TypeContext = {
    none: "none",
    templateLiteralElement: "templateLiteralElement", // `${here}`
    arrayElement: "arrayElement", // here[]
    indexedAccessElement: "indexedAccessElement", // {}[here]
    conditionalCheck: "conditionalCheck", // here extends 1 ? 2 : 3
    conditionalExtends: "conditionalExtends", // 1 extends here ? 2 : 3
    conditionalTrue: "conditionalTrue", // 1 extends 2 ? here : 3
    conditionalFalse: "conditionalFalse", // 1 extends 2 ? 3 : here
    indexedIndex: "indexedIndex", // {}[here]
    indexedObject: "indexedObject", // here[1]
    inferredConstraint: "inferredConstraint", // 1 extends infer X extends here ? 1 : 2
    intersectionElement: "intersectionElement", // here & 1
    mappedName: "mappedName", // { [k in string as here]: 1 }
    mappedParameter: "mappedParameter", // { [k in here]: 1 }
    mappedTemplate: "mappedTemplate", // { [k in string]: here }
    optionalElement: "optionalElement", // [here?]
    predicateTarget: "predicateTarget", // (): X is here
    queryTypeTarget: "queryTypeTarget", // typeof here, can only ever be a ReferenceType
    typeOperatorTarget: "typeOperatorTarget", // keyof here
    referenceTypeArgument: "referenceTypeArgument", // X<here>
    restElement: "restElement", // [...here]
    tupleElement: "tupleElement", // [here]
    unionElement: "unionElement", // here | 1
};
/**
 * Represents an array type.
 *
 * ```ts
 * let value: string[];
 * ```
 * @category Types
 */
export class ArrayType extends Type {
    elementType;
    type = "array";
    /**
     * @param elementType The type of the elements in the array.
     */
    constructor(elementType) {
        super();
        this.elementType = elementType;
    }
    getTypeString() {
        return this.elementType.stringify(TypeContext.arrayElement) + "[]";
    }
    needsParenthesis() {
        return false;
    }
    toObject(serializer) {
        return {
            type: this.type,
            elementType: serializer.toObject(this.elementType),
        };
    }
}
/**
 * Represents a conditional type.
 *
 * ```ts
 * let value: Check extends Extends ? True : False;
 * ```
 * @category Types
 */
export class ConditionalType extends Type {
    checkType;
    extendsType;
    trueType;
    falseType;
    type = "conditional";
    constructor(checkType, extendsType, trueType, falseType) {
        super();
        this.checkType = checkType;
        this.extendsType = extendsType;
        this.trueType = trueType;
        this.falseType = falseType;
    }
    getTypeString() {
        return [
            this.checkType.stringify(TypeContext.conditionalCheck),
            "extends",
            this.extendsType.stringify(TypeContext.conditionalExtends),
            "?",
            this.trueType.stringify(TypeContext.conditionalTrue),
            ":",
            this.falseType.stringify(TypeContext.conditionalFalse),
        ].join(" ");
    }
    needsParenthesis(context) {
        const map = {
            none: false,
            templateLiteralElement: false,
            arrayElement: true,
            indexedAccessElement: false,
            conditionalCheck: true,
            conditionalExtends: true,
            conditionalTrue: false,
            conditionalFalse: false,
            indexedIndex: false,
            indexedObject: true,
            inferredConstraint: true,
            intersectionElement: true,
            mappedName: false,
            mappedParameter: false,
            mappedTemplate: false,
            optionalElement: true,
            predicateTarget: false,
            queryTypeTarget: false,
            typeOperatorTarget: true,
            referenceTypeArgument: false,
            restElement: true,
            tupleElement: false,
            unionElement: true,
        };
        return map[context];
    }
    toObject(serializer) {
        return {
            type: this.type,
            checkType: serializer.toObject(this.checkType),
            extendsType: serializer.toObject(this.extendsType),
            trueType: serializer.toObject(this.trueType),
            falseType: serializer.toObject(this.falseType),
        };
    }
}
/**
 * Represents an indexed access type.
 * @category Types
 */
export class IndexedAccessType extends Type {
    objectType;
    indexType;
    type = "indexedAccess";
    constructor(objectType, indexType) {
        super();
        this.objectType = objectType;
        this.indexType = indexType;
    }
    getTypeString() {
        return [
            this.objectType.stringify(TypeContext.indexedObject),
            "[",
            this.indexType.stringify(TypeContext.indexedIndex),
            "]",
        ].join("");
    }
    needsParenthesis() {
        return false;
    }
    toObject(serializer) {
        return {
            type: this.type,
            indexType: serializer.toObject(this.indexType),
            objectType: serializer.toObject(this.objectType),
        };
    }
}
/**
 * Represents an inferred type, U in the example below.
 *
 * ```ts
 * type Z = Promise<string> extends Promise<infer U> : never
 * ```
 * @category Types
 */
export class InferredType extends Type {
    name;
    constraint;
    type = "inferred";
    constructor(name, constraint) {
        super();
        this.name = name;
        this.constraint = constraint;
    }
    getTypeString() {
        if (this.constraint) {
            return `infer ${this.name} extends ${this.constraint.stringify(TypeContext.inferredConstraint)}`;
        }
        return `infer ${this.name}`;
    }
    needsParenthesis(context) {
        const map = {
            none: false,
            templateLiteralElement: false,
            arrayElement: true,
            indexedAccessElement: false,
            conditionalCheck: false,
            conditionalExtends: false,
            conditionalTrue: false,
            conditionalFalse: false,
            indexedIndex: false,
            indexedObject: true,
            inferredConstraint: false,
            intersectionElement: false,
            mappedName: false,
            mappedParameter: false,
            mappedTemplate: false,
            optionalElement: true,
            predicateTarget: false,
            queryTypeTarget: false,
            typeOperatorTarget: false,
            referenceTypeArgument: false,
            restElement: true,
            tupleElement: false,
            unionElement: false,
        };
        return map[context];
    }
    toObject(serializer) {
        return {
            type: this.type,
            name: this.name,
            constraint: serializer.toObject(this.constraint),
        };
    }
}
/**
 * Represents an intersection type.
 *
 * ```ts
 * let value: A & B;
 * ```
 * @category Types
 */
export class IntersectionType extends Type {
    types;
    type = "intersection";
    constructor(types) {
        super();
        this.types = types;
    }
    getTypeString() {
        return this.types
            .map((t) => t.stringify(TypeContext.intersectionElement))
            .join(" & ");
    }
    needsParenthesis(context) {
        const map = {
            none: false,
            templateLiteralElement: false,
            arrayElement: true,
            indexedAccessElement: false,
            conditionalCheck: true,
            conditionalExtends: false,
            conditionalTrue: false,
            conditionalFalse: false,
            indexedIndex: false,
            indexedObject: true,
            inferredConstraint: false,
            intersectionElement: false,
            mappedName: false,
            mappedParameter: false,
            mappedTemplate: false,
            optionalElement: true,
            predicateTarget: false,
            queryTypeTarget: false,
            typeOperatorTarget: true,
            referenceTypeArgument: false,
            restElement: true,
            tupleElement: false,
            unionElement: false,
        };
        return map[context];
    }
    toObject(serializer) {
        return {
            type: this.type,
            types: this.types.map((t) => serializer.toObject(t)),
        };
    }
}
/**
 * Represents an intrinsic type like `string` or `boolean`.
 *
 * ```ts
 * let value: number;
 * ```
 * @category Types
 */
export class IntrinsicType extends Type {
    name;
    type = "intrinsic";
    constructor(name) {
        super();
        this.name = name;
    }
    getTypeString() {
        return this.name;
    }
    toObject() {
        return {
            type: this.type,
            name: this.name,
        };
    }
    needsParenthesis() {
        return false;
    }
}
/**
 * Represents a literal type.
 *
 * ```ts
 * type A = "A"
 * type B = 1
 * ```
 * @category Types
 */
export class LiteralType extends Type {
    value;
    type = "literal";
    constructor(value) {
        super();
        this.value = value;
    }
    /**
     * Return a string representation of this type.
     */
    getTypeString() {
        if (typeof this.value === "bigint") {
            return this.value.toString() + "n";
        }
        return JSON.stringify(this.value);
    }
    needsParenthesis() {
        return false;
    }
    toObject() {
        if (typeof this.value === "bigint") {
            return {
                type: this.type,
                value: {
                    value: this.value.toString().replace("-", ""),
                    negative: this.value < BigInt("0"),
                },
            };
        }
        return {
            type: this.type,
            value: this.value,
        };
    }
}
/**
 * Represents a mapped type.
 *
 * ```ts
 * { -readonly [K in Parameter as Name]?: Template }
 * ```
 * @category Types
 */
export class MappedType extends Type {
    parameter;
    parameterType;
    templateType;
    readonlyModifier;
    optionalModifier;
    nameType;
    type = "mapped";
    constructor(parameter, parameterType, templateType, readonlyModifier, optionalModifier, nameType) {
        super();
        this.parameter = parameter;
        this.parameterType = parameterType;
        this.templateType = templateType;
        this.readonlyModifier = readonlyModifier;
        this.optionalModifier = optionalModifier;
        this.nameType = nameType;
    }
    getTypeString() {
        const read = {
            "+": "readonly ",
            "-": "-readonly ",
            "": "",
        }[this.readonlyModifier ?? ""];
        const opt = {
            "+": "?",
            "-": "-?",
            "": "",
        }[this.optionalModifier ?? ""];
        const parts = [
            "{ ",
            read,
            "[",
            this.parameter,
            " in ",
            this.parameterType.stringify(TypeContext.mappedParameter),
        ];
        if (this.nameType) {
            parts.push(" as ", this.nameType.stringify(TypeContext.mappedName));
        }
        parts.push("]", opt, ": ", this.templateType.stringify(TypeContext.mappedTemplate), " }");
        return parts.join("");
    }
    needsParenthesis() {
        return false;
    }
    toObject(serializer) {
        return {
            type: this.type,
            parameter: this.parameter,
            parameterType: serializer.toObject(this.parameterType),
            templateType: serializer.toObject(this.templateType),
            readonlyModifier: this.readonlyModifier,
            optionalModifier: this.optionalModifier,
            nameType: serializer.toObject(this.nameType),
        };
    }
}
/**
 * Represents an optional type
 * ```ts
 * type Z = [1, 2?]
 * //           ^^
 * ```
 * @category Types
 */
export class OptionalType extends Type {
    elementType;
    type = "optional";
    constructor(elementType) {
        super();
        this.elementType = elementType;
    }
    getTypeString() {
        return this.elementType.stringify(TypeContext.optionalElement) + "?";
    }
    needsParenthesis() {
        return false;
    }
    toObject(serializer) {
        return {
            type: this.type,
            elementType: serializer.toObject(this.elementType),
        };
    }
}
/**
 * Represents a type predicate.
 *
 * ```ts
 * function isString(x: unknown): x is string {}
 * function assert(condition: boolean): asserts condition {}
 * ```
 * @category Types
 */
export class PredicateType extends Type {
    name;
    asserts;
    targetType;
    type = "predicate";
    /**
     * Create a new PredicateType instance.
     *
     * @param name The identifier name which is tested by the predicate.
     * @param asserts True if the type is of the form `asserts val is string`,
     *                false if the type is of the form `val is string`
     * @param targetType The type that the identifier is tested to be.
     *                   May be undefined if the type is of the form `asserts val`.
     *                   Will be defined if the type is of the form `asserts val is string` or `val is string`.
     */
    constructor(name, asserts, targetType) {
        super();
        this.name = name;
        this.asserts = asserts;
        this.targetType = targetType;
    }
    /**
     * Return a string representation of this type.
     */
    getTypeString() {
        const out = this.asserts ? ["asserts", this.name] : [this.name];
        if (this.targetType) {
            out.push("is", this.targetType.stringify(TypeContext.predicateTarget));
        }
        return out.join(" ");
    }
    needsParenthesis() {
        return false;
    }
    toObject(serializer) {
        return {
            type: this.type,
            name: this.name,
            asserts: this.asserts,
            targetType: serializer.toObject(this.targetType),
        };
    }
}
/**
 * Represents a type that is constructed by querying the type of a reflection.
 * ```ts
 * const x = 1
 * type Z = typeof x // query on reflection for x
 * ```
 * @category Types
 */
export class QueryType extends Type {
    queryType;
    type = "query";
    constructor(queryType) {
        super();
        this.queryType = queryType;
    }
    getTypeString() {
        return `typeof ${this.queryType.stringify(TypeContext.queryTypeTarget)}`;
    }
    /**
     * @privateRemarks
     * An argument could be made that this ought to return true for indexedObject
     * since precedence is different than on the value side... if someone really cares
     * they can easily use a custom theme to change this.
     */
    needsParenthesis() {
        return false;
    }
    toObject(serializer) {
        return {
            type: this.type,
            queryType: serializer.toObject(this.queryType),
        };
    }
}
/**
 * Represents a type that refers to another reflection like a class, interface or enum.
 *
 * ```ts
 * let value: MyClass<T>;
 * ```
 * @category Types
 */
let ReferenceType = (() => {
    let _classSuper = Type;
    let __project_decorators;
    let __project_initializers = [];
    let __project_extraInitializers = [];
    return class ReferenceType extends _classSuper {
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            __project_decorators = [NonEnumerable];
            __esDecorate(null, null, __project_decorators, { kind: "field", name: "_project", static: false, private: false, access: { has: obj => "_project" in obj, get: obj => obj._project, set: (obj, value) => { obj._project = value; } }, metadata: _metadata }, __project_initializers, __project_extraInitializers);
            if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        }
        type = "reference";
        /**
         * The name of the referenced type.
         *
         * If the symbol cannot be found because it's not part of the documentation this
         * can be used to represent the type.
         */
        name;
        /**
         * The type arguments of this reference.
         */
        typeArguments;
        /**
         * The resolved reflection.
         */
        get reflection() {
            if (typeof this._target === "number") {
                return this._project?.getReflectionById(this._target);
            }
            const resolvePotential = this._project?.getReflectionsFromSymbolId(this._target);
            if (!resolvePotential?.length) {
                return;
            }
            const kind = this.preferValues
                ? ReflectionKind.ValueReferenceTarget
                : ReflectionKind.TypeReferenceTarget;
            const resolved = resolvePotential.find((refl) => refl.kindOf(kind)) ||
                resolvePotential.find((refl) => refl.kindOf(~kind));
            // Do not mark the type as resolved at this point so that if it
            // points to a member which is removed (e.g. by typedoc-plugin-zod)
            // and then replaced it still ends up pointing at the right reflection.
            // We will lock type reference resolution when serializing to JSON.
            // this._target = resolved.id;
            return resolved;
        }
        /**
         * Sometimes a few properties are more important than the rest
         * of the properties within a type. This occurs most often with
         * object parameters, where users want to specify `@param foo.bar`
         * to highlight something about the `bar` property.
         *
         * Does NOT support nested properties.
         */
        highlightedProperties;
        /**
         * If not resolved, the symbol id of the reflection, otherwise undefined.
         */
        get symbolId() {
            if (!this.reflection && typeof this._target === "object") {
                return this._target;
            }
        }
        /**
         * Checks if this type is a reference type because it uses a name, but is intentionally not pointing
         * to a reflection. This happens for type parameters and when representing a mapped type.
         */
        isIntentionallyBroken() {
            if (typeof this._target === "object" &&
                this._project?.symbolIdHasBeenRemoved(this._target)) {
                return true;
            }
            return this._target === -1 || this.refersToTypeParameter;
        }
        /**
         * Convert this reference type to a declaration reference used for resolution of external types.
         */
        toDeclarationReference() {
            return {
                resolutionStart: "global",
                moduleSource: this.package,
                symbolReference: {
                    path: this.qualifiedName
                        .split(".")
                        .map((p) => ({ path: p, navigation: "." })),
                },
            };
        }
        /**
         * The fully qualified name of the referenced type, relative to the file it is defined in.
         * This will usually be the same as `name`, unless namespaces are used.
         */
        qualifiedName;
        /**
         * The package that this type is referencing.
         */
        package;
        /**
         * If this reference type refers to a reflection defined by a project not being rendered,
         * points to the url that this type should be linked to.
         */
        externalUrl;
        /**
         * If set, no warnings about something not being exported should be created
         * since this may be referring to a type created with `infer X` which will not
         * be registered on the project.
         */
        refersToTypeParameter = false;
        /**
         * If set, will prefer reflections with {@link ReflectionKind | ReflectionKinds} which represent
         * values rather than those which represent types.
         */
        preferValues = false;
        _target;
        _project = __runInitializers(this, __project_initializers, void 0);
        constructor(name, target, project, qualifiedName) {
            super();
            __runInitializers(this, __project_extraInitializers);
            this.name = name;
            if (typeof target === "number") {
                this._target = target;
            }
            else {
                this._target = "variant" in target ? target.id : target;
            }
            this._project = project;
            this.qualifiedName = qualifiedName;
        }
        static createUnresolvedReference(name, target, project, qualifiedName) {
            return new ReferenceType(name, target, project, qualifiedName);
        }
        static createResolvedReference(name, target, project) {
            return new ReferenceType(name, target, project, name);
        }
        /**
         * This is used for type parameters, which don't actually point to something,
         * and also for temporary references which will be cleaned up with real references
         * later during conversion.
         * @internal
         */
        static createBrokenReference(name, project) {
            return new ReferenceType(name, -1, project, name);
        }
        getTypeString() {
            const name = this.reflection ? this.reflection.name : this.name;
            let typeArgs = "";
            if (this.typeArguments && this.typeArguments.length > 0) {
                typeArgs += "<";
                typeArgs += this.typeArguments
                    .map((arg) => arg.stringify(TypeContext.referenceTypeArgument))
                    .join(", ");
                typeArgs += ">";
            }
            return name + typeArgs;
        }
        needsParenthesis() {
            return false;
        }
        toObject(serializer) {
            let target;
            if (typeof this._target === "number") {
                target = this._target;
            }
            else if (this._project?.symbolIdHasBeenRemoved(this._target)) {
                target = -1;
            }
            else if (this.reflection) {
                target = this.reflection.id;
            }
            else {
                target = this._target.toObject();
            }
            const result = {
                type: this.type,
                target,
                typeArguments: serializer.toObjectsOptional(this.typeArguments),
                name: this.name,
                package: this.package,
                externalUrl: this.externalUrl,
            };
            if (this.name !== this.qualifiedName) {
                result.qualifiedName = this.qualifiedName;
            }
            if (this.refersToTypeParameter) {
                result.refersToTypeParameter = true;
            }
            if (typeof target !== "number" && this.preferValues) {
                result.preferValues = true;
            }
            if (this.highlightedProperties) {
                result.highlightedProperties = Object.fromEntries(Array.from(this.highlightedProperties.entries(), ([key, parts]) => {
                    return [
                        key,
                        Comment.serializeDisplayParts(parts),
                    ];
                }));
            }
            return result;
        }
        fromObject(de, obj) {
            this.typeArguments = de.reviveMany(obj.typeArguments, (t) => de.constructType(t));
            if (typeof obj.target === "number" && obj.target !== -1) {
                de.defer((project) => {
                    const target = project.getReflectionById(de.oldIdToNewId[obj.target] ?? -1);
                    if (target) {
                        this._project = project;
                        this._target = target.id;
                    }
                    else {
                        de.logger.warn(i18n.serialized_project_referenced_0_not_part_of_project(JSON.stringify(obj.target)));
                    }
                });
            }
            else if (obj.target === -1) {
                this._target = -1;
            }
            else {
                this._project = de.project;
                this._target = new ReflectionSymbolId(obj.target);
            }
            this.qualifiedName = obj.qualifiedName ?? obj.name;
            this.package = obj.package;
            this.refersToTypeParameter = !!obj.refersToTypeParameter;
            this.preferValues = !!obj.preferValues;
            if (obj.highlightedProperties) {
                this.highlightedProperties = new Map();
                for (const [key, parts] of Object.entries(obj.highlightedProperties)) {
                    this.highlightedProperties.set(key, Comment.deserializeDisplayParts(de, parts));
                }
            }
        }
    };
})();
export { ReferenceType };
/**
 * Represents a type which has it's own reflection like literal types.
 * This type will likely go away at some point and be replaced by a dedicated
 * `ObjectType`. Allowing reflections to be nested within types causes much
 * pain in the rendering code.
 *
 * ```ts
 * let value: { a: string, b: number };
 * ```
 * @category Types
 */
export class ReflectionType extends Type {
    declaration;
    type = "reflection";
    constructor(declaration) {
        super();
        this.declaration = declaration;
    }
    getTypeString() {
        const parts = [];
        const sigs = this.declaration.getNonIndexSignatures();
        for (const sig of sigs) {
            parts.push(sigStr(sig, ": "));
        }
        for (const p of this.declaration.children || []) {
            parts.push(`${p.name}${propertySep(p)} ${typeStr(p.type)}`);
        }
        for (const s of this.declaration.indexSignatures || []) {
            parts.push(sigStr(s, ": ", "[]"));
        }
        if (sigs.length === 1 && parts.length === 1) {
            return sigStr(sigs[0], " => ");
        }
        if (parts.length === 0) {
            return "{}";
        }
        return `{ ${parts.join("; ")} }`;
    }
    needsParenthesis(where) {
        if (this.declaration.children || this.declaration.indexSignatures) {
            return false;
        }
        if (this.declaration.signatures?.length === 1) {
            return where === TypeContext.arrayElement || where === TypeContext.unionElement;
        }
        return false;
    }
    toObject(serializer) {
        return {
            type: this.type,
            declaration: serializer.toObject(this.declaration),
        };
    }
}
/**
 * Represents a rest type
 * ```ts
 * type Z = [1, ...2[]]
 * //           ^^^^^^
 * ```
 * @category Types
 */
export class RestType extends Type {
    elementType;
    type = "rest";
    constructor(elementType) {
        super();
        this.elementType = elementType;
    }
    getTypeString() {
        return `...${this.elementType.stringify(TypeContext.restElement)}`;
    }
    needsParenthesis() {
        return false;
    }
    toObject(serializer) {
        return {
            type: this.type,
            elementType: serializer.toObject(this.elementType),
        };
    }
}
/**
 * TS 4.1 template literal types
 * ```ts
 * type Z = `${'a' | 'b'}${'a' | 'b'}`
 * ```
 * @category Types
 */
export class TemplateLiteralType extends Type {
    head;
    tail;
    type = "templateLiteral";
    constructor(head, tail) {
        super();
        this.head = head;
        this.tail = tail;
    }
    getTypeString() {
        return [
            "`",
            this.head,
            ...this.tail.map(([type, text]) => {
                return ("${" +
                    type.stringify(TypeContext.templateLiteralElement) +
                    "}" +
                    text);
            }),
            "`",
        ].join("");
    }
    needsParenthesis() {
        return false;
    }
    toObject(serializer) {
        return {
            type: this.type,
            head: this.head,
            tail: this.tail.map(([type, text]) => [
                serializer.toObject(type),
                text,
            ]),
        };
    }
}
/**
 * Represents a tuple type.
 *
 * ```ts
 * let value: [string, boolean];
 * ```
 * @category Types
 */
export class TupleType extends Type {
    elements;
    type = "tuple";
    /**
     * @param elements The ordered type elements of the tuple type.
     */
    constructor(elements) {
        super();
        this.elements = elements;
    }
    getTypeString() {
        return ("[" +
            this.elements
                .map((t) => t.stringify(TypeContext.tupleElement))
                .join(", ") +
            "]");
    }
    needsParenthesis() {
        return false;
    }
    toObject(serializer) {
        return {
            type: this.type,
            elements: serializer.toObjectsOptional(this.elements),
        };
    }
}
/**
 * Represents a named member of a tuple type.
 *
 * ```ts
 * let value: [name: string];
 * ```
 * @category Types
 */
export class NamedTupleMember extends Type {
    name;
    isOptional;
    element;
    type = "namedTupleMember";
    constructor(name, isOptional, element) {
        super();
        this.name = name;
        this.isOptional = isOptional;
        this.element = element;
    }
    /**
     * Return a string representation of this type.
     */
    getTypeString() {
        return `${this.name}${this.isOptional ? "?" : ""}: ${this.element.stringify(TypeContext.tupleElement)}`;
    }
    needsParenthesis() {
        return false;
    }
    toObject(serializer) {
        return {
            type: this.type,
            name: this.name,
            isOptional: this.isOptional,
            element: serializer.toObject(this.element),
        };
    }
}
/**
 * Represents a type operator type.
 *
 * ```ts
 * class A {}
 * class B<T extends keyof A> {}
 * ```
 * @category Types
 */
export class TypeOperatorType extends Type {
    target;
    operator;
    type = "typeOperator";
    constructor(target, operator) {
        super();
        this.target = target;
        this.operator = operator;
    }
    getTypeString() {
        return `${this.operator} ${this.target.stringify(TypeContext.typeOperatorTarget)}`;
    }
    needsParenthesis(context) {
        const map = {
            none: false,
            templateLiteralElement: false,
            arrayElement: true,
            indexedAccessElement: false,
            conditionalCheck: false,
            conditionalExtends: false,
            conditionalTrue: false,
            conditionalFalse: false,
            indexedIndex: false,
            indexedObject: true,
            inferredConstraint: false,
            intersectionElement: false,
            mappedName: false,
            mappedParameter: false,
            mappedTemplate: false,
            optionalElement: true,
            predicateTarget: false,
            queryTypeTarget: false,
            typeOperatorTarget: false,
            referenceTypeArgument: false,
            restElement: false,
            tupleElement: false,
            unionElement: false,
        };
        return map[context];
    }
    toObject(serializer) {
        return {
            type: this.type,
            operator: this.operator,
            target: serializer.toObject(this.target),
        };
    }
}
/**
 * Represents an union type.
 *
 * ```ts
 * let value: string | string[];
 * ```
 * @category Types
 */
export class UnionType extends Type {
    types;
    type = "union";
    /**
     * If present, there should be as many items in this array as there are items in the {@link types} array.
     *
     * This member is only valid on unions which are on {@link DeclarationReflection.type | DeclarationReflection.type} with a
     * {@link ReflectionKind} `kind` of `TypeAlias`. Specifying it on any other union is undefined behavior.
     */
    elementSummaries;
    constructor(types) {
        super();
        this.types = types;
    }
    getTypeString() {
        return this.types
            .map((t) => t.stringify(TypeContext.unionElement))
            .join(" | ");
    }
    needsParenthesis(context) {
        const map = {
            none: false,
            templateLiteralElement: false,
            arrayElement: true,
            indexedAccessElement: false,
            conditionalCheck: true,
            conditionalExtends: false,
            conditionalTrue: false,
            conditionalFalse: false,
            indexedIndex: false,
            indexedObject: true,
            inferredConstraint: false,
            intersectionElement: true,
            mappedName: false,
            mappedParameter: false,
            mappedTemplate: false,
            optionalElement: true,
            predicateTarget: false,
            queryTypeTarget: false,
            typeOperatorTarget: true,
            referenceTypeArgument: false,
            restElement: false,
            tupleElement: false,
            unionElement: false,
        };
        return map[context];
    }
    fromObject(de, obj) {
        if (obj.elementSummaries) {
            this.elementSummaries = obj.elementSummaries.map((parts) => Comment.deserializeDisplayParts(de, parts));
        }
    }
    toObject(serializer) {
        return {
            type: this.type,
            types: this.types.map((t) => serializer.toObject(t)),
            elementSummaries: this.elementSummaries?.map((parts) => Comment.serializeDisplayParts(parts)),
        };
    }
}
/**
 * Represents all unknown types that cannot be converted by TypeDoc.
 * @category Types
 */
export class UnknownType extends Type {
    type = "unknown";
    /**
     * A string representation of the type as returned from TypeScript compiler.
     */
    name;
    constructor(name) {
        super();
        this.name = name;
    }
    getTypeString() {
        return this.name;
    }
    /**
     * Always returns true if not at the root level, we have no idea what's in here, so wrap it in parenthesis
     * to be extra safe.
     */
    needsParenthesis(context) {
        return context !== TypeContext.none;
    }
    toObject() {
        return {
            type: this.type,
            name: this.name,
        };
    }
}
function propertySep(refl) {
    return refl.flags.isOptional ? "?:" : ":";
}
function typeStr(type) {
    return type?.toString() ?? "any";
}
function sigStr(sig, sep, brackets = "()") {
    const params = joinArray(sig.parameters, ", ", (p) => `${p.name}${propertySep(p)} ${typeStr(p.type)}`);
    return `${brackets[0]}${params}${brackets[1]}${sep}${typeStr(sig.type)}`;
}
