Skip to content

Commit

Permalink
Fix "Type of property circularly references itself in mapped type" er…
Browse files Browse the repository at this point in the history
…rors
  • Loading branch information
LukeAbby committed Oct 19, 2024
1 parent 0d49bd6 commit cf3a3bb
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 4 deletions.
16 changes: 13 additions & 3 deletions src/foundry/client/data/abstract/client-document.d.mts
Original file line number Diff line number Diff line change
Expand Up @@ -554,11 +554,14 @@ declare global {
* A mixin which extends each Document definition with specialized client-side behaviors.
* This mixin defines the client-side interface for database operations and common document behaviors.
*/
// Note: Unlike most mixins, `ClientDocumentMixin` actually requires a specific constructor, the same as `Document`.
// This means that `BaseClass extends Document.AnyConstructor` would be too permissive.
// FIXME(LukeAbby): Unlike most mixins, `ClientDocumentMixin` actually requires a specific constructor, the same as `Document`.
// This means that `BaseClass extends Document.Internal.Constructor` is actually too permissive.
// However this easily leads to circularities.
//
// Note(LukeAbby): The seemingly redundant merging in of `AnyDocument` makes it easier for tsc to recognize that anything extending `ClientDocumentMixin` is also a document.
function ClientDocumentMixin<BaseClass extends Document.Internal.Constructor>(
Base: BaseClass,
): Mixin<typeof ClientDocument<InstanceType<BaseClass>>, BaseClass>;
): AnyDocument & Mixin<typeof ClientDocument<InstanceType<BaseClass>>, BaseClass>;

namespace ClientDocument {
interface SortOptions<T, SortKey extends string = "sort"> extends SortingHelpers.SortOptions<T, SortKey> {
Expand Down Expand Up @@ -645,6 +648,13 @@ declare namespace DropData {
}
}

// This is yet another `AnyDocument` type.
// It exists specifically because the `Document.AnyConstructor` type is too safe to be merged in with a mixin.
// The `arg0: never, ...args: never[]` trick trips up the base constructor check and so this one with an actual `...args: any[]` one is used instead.
declare class AnyDocument extends Document<any, any, any> {
constructor(...args: any[]);
}

type InternalData<T extends Document.Internal.Instance.Any> = DropData.Data<Document.Internal.Instance.Complete<T>>;

interface FromDropDataOptions {
Expand Down
2 changes: 1 addition & 1 deletion src/foundry/common/abstract/document.d.mts
Original file line number Diff line number Diff line change
Expand Up @@ -882,7 +882,7 @@ declare namespace Document {

type ConfiguredClass<T extends { metadata: AnyMetadata }> = ConfiguredClassForName<T["metadata"]["name"]>;

type ConfiguredClassForName<Name extends Type> = ConfiguredDocuments[Name];
type ConfiguredClassForName<Name extends Type> = MakeConform<ConfiguredDocuments[Name], Document.AnyConstructor>;

type SubTypesOf<T extends Type> =
ConfiguredInstanceForName<T> extends { type: infer Types } ? Types : typeof foundry.CONST.BASE_DOCUMENT_TYPE;
Expand Down

0 comments on commit cf3a3bb

Please sign in to comment.