UNPKG

@league-of-foundry-developers/foundry-vtt-types

Version:
1,142 lines (1,066 loc) 59.7 kB
import type { Document } from "../foundry/common/abstract/_module.d.mts"; // eslint-disable-next-line @typescript-eslint/no-empty-object-type type ConfiguredModuleData<Name extends string> = Name extends keyof ModuleConfig ? ModuleConfig[Name] : {}; /** * This type exists due to https://github.com/microsoft/TypeScript/issues/55667 * This will be deprecated once this issue is solved. */ export type FixedInstanceType<T extends abstract new (...args: never) => any> = T extends abstract new ( ...args: infer _ ) => infer R ? R : never; /** @deprecated Replaced with {@linkcode foundry.packages.Module.ForName | Module.ForName}, will be removed in v14 */ export type ConfiguredModule<Name extends string> = Name extends keyof RequiredModules ? ConfiguredModuleData<Name> : | ({ active: true } & ConfiguredModuleData<Name>) // flawed, can't use `key in module` this way, but omitting the Partial Record type kills nullish // collocating, which is probably the better DX. | ({ active: false } & Record<keyof ConfiguredModuleData<Name>, undefined>); /** Keys of functions of console.log / globalThis.logger */ export type LoggingLevels = "debug" | "log" | "info" | "warn" | "error"; /** * `GetKey` accesses a property while intentionally ignoring index signatures. This means `GetKey<Record<string, unknown>, "foo">` will return `never`. */ // Note(LukeAbby): There are five tricky cases: // - `T = {}` and `T = object` could easily return `unknown` and must be excluded. // - `T = never` might either distribute out or return `unknown`. The fix here is checking if `any` is assignable to it. // - `T = { prop?: U }` with `exactOptionalPropertyTypes` should return `U | undefined` not `U`. // - `T` has getters `GetKey` should still access it, this means checking `keyof T` is not helpful. export type GetKey<T, K extends PropertyKey, D = never> = [object] extends [T] // Handle `{}` and `object` ? D : [any] extends [T] // Handle never ? _GetKey<T, K, D> : D; // Note(LukeAbby): This uses `infer _V` specifically to avoid index signatures. // However it isn't `T extends { readonly [_ in K]?: infer V } ? V : D` as under // `exactOptionalPropertyTypes` this would not include `undefined` whereas `T[K]` does. type _GetKey<T, K extends PropertyKey, D> = T extends { readonly [_ in K]?: infer _V } ? T[K] : D; /** * `Partial` is usually the wrong type. * In order to make it easier to audit unintentional uses of `Partial` this type is provided. * Also allows specifying certain keys to make partial. * * ### Picking the right helper type * - Favor `NullishProps` whenever it is valid. Allowing both `null` and * `undefined` is convenient for the end user and it is very common that * wherever `undefined` is valid so is `null`. For some examples it is valid * for `options.prop ??= "default"`, `options.prop ||= "default"`, * `if (options.prop) { ... }`, `if (options.prop == null)`, or so on. * - Use `IntentionalPartial` when an explicit `undefined` is problematic but * leaving off the property entirely is fine. This primarily occurs when * patterns like `options = { ...defaultOptions, ...options }`, * `Object.assign({}, defaultOptions, options)`, * `foundry.utils.mergeObject(defaultOptions, options)`, or so on. * * Note that {@linkcode foundry.utils.mergeObject} * also expands the object. So once `ExpandsTo` exists you should also use * that helper type. * * What these patterns have in common is that if `options` looks like * `{ prop: undefined }` that will override whatever is in `defaultOptions` * and may cause issues. Note that even if you see one of these patterns you * also need to ensure that `undefined` would cause issues down the road * before using `IntentionalPartial` as it could be an intended way of * resetting a property. * - Use `InexactPartial` when `null` is problematic but `undefined` is not. * The most common time this shows up is with the pattern * `exampleFunction({ prop = "foo" } = {}) { ... }`. */ export type IntentionalPartial<T extends object> = Partial<T>; /** * This type is used to make a constraint where `T` must be statically known to overlap with `U`. * * @example * ```ts * // The `const T` allows inference to be a bit more specific. This is useful for a utility type like this.` * function takesNumber<const T>(input: OverlapsWith<T, number>): void { * // This function body is an example of a method this might be useful for. * // If the input isn't an number it simply returns in this case. * if (typeof input !== "number") { * return; * } * * // Assumes, unchecked, that `element` is a number. * // This means an input like `number[] | string[]` would be unsound as it could be a string. * element + 1; * } * * takesNumber(1); // Ok! * takesNumber("foo"); // Error, statically known to never an number and so presumed to be a mistake. * takesNumber(Math.random() > 0.5 ? 1 : "foo"); // Ok, `"foo"` doesn't actually cause any runtime issues, it was just disallowed above because then it'd never do anything useful. * ``` */ export type OverlapsWith<T, U> = [Extract<T, U>, any] extends [U, Extract<T, U>] ? T : U extends T ? T : U; /** * Used to build a constraint where `T` to overlap with `Item[]` but disallows unrelated arrays. * This is safer than what `OverlapsWith` provides as it ensures that if the type is an array it is an array of `Item`. * Assumes readonly arrays are permitted. * * Note that `never[]` and `any[]` are still accepted due to the fundamental nature of these types. * * @example * ```ts * // The `const T` allows inference to be a bit more specific. This is useful for a utility type like this.` * function takesNumericArray<const T>(input: ArrayOverlaps<T, number>): void { * // This function body is an example of a method this might be useful for. * // If the input isn't an array it simply returns in this case. * if (!Array.isArray(input)) { * return; * } * * for (const element of input) { * // Assumes, unchecked, that `element` is a number. * // This means an input like `number[] | string[]` would be unsound as it could be a string. * element + 1; * } * } * * takesNumericArray([1, 2, 3]); // Ok! * takesNumericArray("foo"); // Error, statically known to never an array and so presumed to be a mistake. * takesNumericArray(Math.random() > 0.5 ? [1, 2, 3] : "foo"); // Ok, `"foo"` doesn't actually cause any runtime issues, it was just disallowed above because then it'd never do anything useful. * takesNumericArray(Math.random() > 0.5 ? [1, 2, 3] : ["foo", "bar"]); // Error, at runtime it could be an array of the wrong type and that isn't handled. Notably this would succeed with `OverlapsWith`. * ``` */ export type ArrayOverlaps<Arr, T> = Extract<Arr, readonly unknown[]> extends readonly T[] ? OverlapsWith<Arr, readonly T[]> : readonly T[]; /** * Use this whenever a type is given that should match some constraint but is * not guaranteed to. For example when additional properties can be declaration * merged into an interface. When the type does not conform then `ConformTo` is * used instead. * * See `MustConform` for a version that throws a compilation error when the type * cannot be statically known to conform. */ export type MakeConform<T, ConformTo, D extends ConformTo = ConformTo> = [T] extends [ConformTo] ? T : D; /** * This is useful when you want to ensure that a type conforms to a certain * constraint. If it is not guaranteed to conform then a compilation error is * thrown. This makes it too conservative in some cases. */ export type MustConform<T extends ConformTo, ConformTo> = T; /** * This allows you to treat all interfaces as a plain object. But beware, if the * interface represents a function, array, or constructor then these will be * stripped from the interface. * * This is generally intended for cases where an interface is given in order to * be declaration merged and then must be assigned to a plain object type. * * The constraint `T extends object` is used because `object` includes functions * and arrays etc. This is crucial to allow interfaces to be given to this type. */ export type InterfaceToObject<T extends object> = { // This mapped type would be a no-op on most types (even primitives like string) // but for functions, classes, and arrays they convert them to "proper" objects by // stripping constructors/function signatures. One side effect is a type like // `() => number` will result in `{}`. [K in keyof T]: T[K]; }; /** * This is a helper type that allows you to ensure that a record conforms to a * certain shape. This is useful when you want to ensure that a record has all * keys of a certain type. * * When a value does not conform it is replaced with `never` to indicate that * there is an issue. */ export type ConformRecord<T extends object, V, D extends V = V> = { [K in keyof T]: T[K] extends V ? T[K] : D; }; /** * Converts a regular function type into a function derived from a method. * * Methods have a special exception in TypeScript that allows unsound subtyping * that unfortunately has been deeply engrained into not just JavaScript codebases * but the core APIs of JavaScript itself in the DOM. * * It might seem odd to want to opt-in to this unsoundness but it's unfortunately * useful in several cases, such as when you have a property like * `prop: ((arg: Options) => number) | undefined` and you want to meet the expectations * from other similar methods. * * @example * ```typescript * declare class ExampleBaseClass { * // This demonstrates a typical example of where the allowed unsoundness is useful. * methodOne(arg: { x: string }): number; * * // This helps demonstrates an example that may be easier to recognize as unsound. * methodTwo(arg: string): number; * * functionProperty: (arg: string) => number; * methodLikeProperty: ToMethod<(arg: string) => number>; * } * * // TypeScript allows this without any errors. * declare class MethodSubclassing extends ExampleBaseClass { * // It's a very common thing for subclasses to ask for extra properties. * // This appears in the DOM APIs. * methodOne(arg: { x: string; y: string }): number; * * // Only taking `"foo" | "bar"` should seem pretty unsound. * // The above is actually equally unsound but it's less obvious to many people. * methodTwo(arg: "foo" | "bar"): number; * } * * // This is allowed. If it wasn't subclassing would be less useful. * const exampleMethodSubclass: ExampleBaseClass = new MethodSubclassing(); * * // TypeScript does not error here. However at runtime `MethodSubclassing#methodOne` * // will almost certainly error as it has the required property `y`. * // The reason why there's no errors is an intentional unsoundness in TypeScript. * exampleMethodSubclass.methodOne({ x: "foo" }); * * // Similarly this is allowed. * // Both methods show taking arguments that are 'subtypes' of the original. * // In the case of functions this is unsound as demonstrated because in both * // examples you're substituting a function that has to be able to be called * // with a wide variety of arguments with one that will error for many of them. * exampleMethodSubclass.methodTwo("lorem"); * * declare class PropertySubclassing extends ExampleBaseClass { * // This errors right here. This preventative error is because of the prior * // explained unsoundness. It errors here because there's really only 3 * // places to error at compile time to prevent a runtime error: * // 1. At the call site when a subclass is used unsoundly. Unfortunately * // at this point it's too late to know for certain if it's a subclass * // or not. For example there could be a guarded condition to avoid * // subclasses that TypeScript can't possibly track. * // 2. When trying to assign `PropertySubclassing` to `ExampleBaseClass`. * // This would be a feasible alternative but would likely come as a * // surprise as the subclass could have been used for quite a while * // before trying to be assigned to its superclass. * // 3. Error at the definition. This is where TypeScript has chosen to error. * // The error is unfortunately not the most intuitive but it is correct. * functionProperty: (arg: "foo" | "bar") => number; * } * * declare class MethodLikeSubclassing extends ExampleBaseClass { * // This is unsound but by using the `ToMethod` in the parent class it's allowed. * methodLikeProperty: (arg: "foo" | "bar") => number; * } * ``` * * The TypeScript FAQ explains this in a way that may either be intuitive and * explain all lingering questions or be confusing and muddle the waters. * It's also worth mentioning that it claims all function parameters work this way, * this behavior is disabled for functions in most codebases (including this one) * because of the `strictFunctionTypes` compiler flag, implicit under `strict: true`. * See: https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-function-parameters-bivariant * * Note that this does not work well with exotic functions. Unexpected behavior may occur with: * - Overloaded functions. * - Functions with additional properties, e.g. `(() => number) & { prop: string }`. * - Functions of the shape `(...args: never[]) => T`. */ export type ToMethod<T extends AnyFunction> = { method(...args: Parameters<T>): ReturnType<T>; }["method"]; export type MaybeEmpty<T extends AnyObject> = | T | { [K in keyof T]?: never; }; /** * The following uses `extends object` instead of `AnyObject` to allow `O = typeof SomeClass` */ export type PropertiesOfType<O extends object, V> = { // This type is not distributive to avoid `O[PropertiesOfType<O, V>]` not being assignable to `V` [K in keyof O]: O[K] extends V ? K : never; }[keyof O]; declare class Branded<in out BrandName extends string> { #brand: BrandName; } /** * Brands a type such that is behaves just like the input type while preventing * assignment to it. This is useful to create types that indicate a specific * invariant that the type must adhere to that a more basic type wouldn't have. * * Note: You can brand most types but due to its implementation this * helper is incompatible with `any`, `unknown`, and `never`. See "Brand Implementation" * for more details. * * For example enum members can be branded to prevent an arbitrary number from * being mistakenly used in their place: * * @example * ```ts * type NUMBER_ENUM = Brand<number, "NUMBER_ENUM">; * * const NUMBER_ENUM: { * X: NUMBER_ENUM, * Y: NUMBER_ENUM * }; * * function useNumberEnum(value: NUMBER_ENUM) { ... } * usesNumberEnum(NUMBER_ENUM.X); // Works. * usesNumberEnum(1); // Error. * ``` * * ### Brand Implementation * * The fundamental trick of the implementation is that it intersects the base * type with a compile-time only marker property. This marker property will not * exist on the base type and so prevents assignment just like how `{ foo: string }` * can't be assigned to `{ foo: string; bar: number }` because it's missing a property. * * A more basic implementation might look like this: * * ```ts * type Brand<BaseType, BrandName extends string> = BaseType & { brandType: BrandName }; * ``` * * But this has two problems: * - In theory anyone can add this `brandType` property. * - The `brandType` property is accessible and visible, e.g. * `keyof Brand<BaseType, BrandName>` would include `brandType` because it's a visible property. * * The implementation here solves both of these problems by using a private class field. * This class is unexported and so due to the way that private class properties work this * means there is no other way to create a compatible property (outside of `any`). Using a * class also has the added benefit that the type parameter can be specifically marked as * invariant for a bit of extra protection. * * This does mean that `Brand` only works with types where an intersection is meaningful. * These are the problematic types: * - `any` will become `any` still because `any & T` is still `any`. This makes `Brand` useless. * - `never` stays `never` because `never & T` is `never`. This makes `Brand` useless. * - `unknown` becomes `Branded` because `unknown & T` is `T`. This is a problem because `unknown` can be any type, e.g. `number` but `Branded<unknown, BrandName>` is always an object. * * Unfortunately there aren't really good workarounds either. */ export type Brand<BaseType, BrandName extends string> = BaseType & Branded<BrandName>; /** * An at a best effort level expands a type from something complex that shows up like * `DeepPartial<{ x: { y: number } }>` in intellisense to `{ x?: { y?: number } }`. * This is useful for when you want to see what a type looks like in a more human * readable form. * * Using this type is a performance tradeoff, might increase the likelihood of * circularities, and technically in some extremely niche cases changes the type behavior. * ```@example * // The implementation of this type is outside the scope of this example. * // See UnionToIntersection. * type UnionToIntersection<U> = (U extends unknown ? (arg: U) => void : never) extends (arg: infer I) => void ? I : never; * * type ObjectIntersection = UnionToIntersection<{ x: string } | { y: number }>; * // ^ { x: string } & { y: number } * * type PrettyObjectIntersection = PrettifyType<ObjectIntersection>; * // ^ { x: string, y: number } * * function example<T extends { someProp: number } | { anotherProp: string }>(t: T) { * Object.assign(t, { a: "foo" }, { b: 2 }) satisfies ObjectIntersection * Object.assign(t, { a: "foo" }, { b: 2 }) satisfies PrettyObjectIntersection * // ^ Type 'T & { a: string; } & { b: number; }' does not satisfy the expected type '{ a: string; b: number; }'. * // This is an example of changing type behavior. The first line is allowed but the second errors. * // This type of situation will realistically never come up in real code because it's so contrived. * // Note that this difference only appears when generic, specifically `T extends SomeConcreteObject | U`. * // See https://github.com/microsoft/TypeScript/pull/60726 for some context. * } * ``` */ // Note(LukeAbby): This uses `AnyObject` as a constraint rather than in the body due to this circularity: https://tsplay.dev/NDpRRN export type PrettifyType<T extends AnyObject> = T extends unknown ? { [K in keyof T]: T[K]; // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents } & unknown : never; /** * Convert a union of the form `T1 | T2 | T3 | ...` into an intersection of the form `T1 & T2 & T3 & ...`. * * ### Implementation Details * * Breaking this type down into steps evaluation begins with the expression * `U extends unknown ? ... : never`. Note that `U` is a "bare type parameter", * that is written directly as opposed to being wrapped like `U[]`. Because of * this the type is distributive. * * Distributivity means that `U extends unknown ? (arg: U) => void : never` turns the input * of the form `T1 | T2 | T3 | ...` into `((arg: T1) => void) | ((arg: T2) => void) | ((arg: T3) => void)`. * Let's call this new union `FunctionUnion` * * Finally `FunctionUnion extends (arg: infer I) => void ? I : never` is evaluated. * This results in `T1 & T2 & T3 | ...` as promised... but why? Even with distributivity * in play, normally `(T extends unknown ? F<T> : never) extends F<infer T> ? T : never` * would just be a complex way of writing `T`. * * The complete answer is fairly deep and is unlikely to make sense unless you are * already well versed in this area. In particular it lies in what happens when `F<T>` * puts `T` into a contravariant position. In this case inferring `T` back out * requires an intersection effectively because the covariant assignment rules * are flipped. * * That explanation is unlikely to have helped much and so let's run through two * examples. * * First, a refresher: * ```ts * function takesX(arg: { x: number }): number { ... } * function takesY(arg: { y: string }): string { ... } * * let output = ...; * if (Math.random() > 0.5) { * output = takesX({ x: 1 }); * } else { * output = takesY({ y: "example" }); * } * ``` * * What is the best type for `output` in this case? Of course, it'd be `number | string`. * * What about this example? * ```ts * function takesX(arg: { x: number }): number { ... } * function takesY(arg: { y: string }): string { ... } * * let input = ...; * if (Math.random() > 0.5) { * takesX(input); * } else { * takesY(input); * } * ``` * * What is the best type for `input` in this case? It might be tempting to say `{ x: number } | { y: string }` * similarly to how `output` was `number | string`. But that's not quite right. The correct type for `input` is * actually `{ x: number } & { y: string }`. The reason why this is the case is that it's unpredictable whether * `takesX` or `takesY` will be called. This means that `input` must be able to used to call both functions. * * This is analogous to asking these two questions at the type level: * ```ts * type Functions = typeof takesX | typeof takesY; * type Output = Functions extends (...args: any[]) => infer Output ? Output : never; * // ^ number | string * type Input = Functions extends (arg: infer Input) => any ? Input : never; * // ^ { x: number } & { y: string } * ``` * * And if you reflect on the inciting code, `FunctionUnion extends (arg: infer I) => void ? I : never` * you'll see that it's effectively doing the same thing. * * If you want to read more see TypeScript's handbook section on * [Distributive Conditional Types](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types) * for more information on distributivity. There is also a section on * [Variance](https://www.typescriptlang.org/docs/handbook/2/generics.html#variance-annotations) * in general but it unfortunately doesn't touch too much on specific details and * the emergent behavior of variance like this. */ export type UnionToIntersection<U> = (U extends unknown ? (arg: U) => void : never) extends (arg: infer I) => void ? I : never; // Helper Types /** * Recursively sets keys of an object to optional. Used primarily for update methods. * * Note: This function is intended to work with plain objects. It takes any object only because * otherwise it makes it more difficult to pass in interfaces. * * Its behavior is unspecified when run on a non-plain object. */ // Allowing passing any `object` is done because it's more convenient for the end user. // Note that `{}` should always be assignable to `DeepPartial<T>`. export type DeepPartial<T extends object> = _DeepPartial<T>; type _DeepPartial<T> = T extends object ? T extends AnyArray | AnyFunction | AnyConstructor ? T : { [K in keyof T]?: _DeepPartial<T[K]>; } : T; /** * Gets all possible keys of `T`. This is useful because if `T` is a union type * it will get the properties of all types in the union. Otherwise it functions identically to `keyof T`. */ export type AllKeysOf<T extends object> = T extends unknown ? keyof T : never; /** * Make all properties in `T` optional and explicitly allow `undefined` * * ### Picking the right helper type * - Favor `NullishProps` whenever it is valid. Allowing both `null` and * `undefined` is convenient for the end user and it is very common that * wherever `undefined` is valid so is `null`. For some examples it is valid * for `options.prop ??= "default"`, `options.prop ||= "default"`, * `if (options.prop) { ... }`, `if (options.prop == null)`, or so on. * - Use `IntentionalPartial` when an explicit `undefined` is problematic but * leaving off the property entirely is fine. This primarily occurs when * patterns like `options = { ...defaultOptions, ...options }`, * `Object.assign({}, defaultOptions, options)`, * `foundry.utils.mergeObject(defaultOptions, options)`, or so on. * * Note that {@linkcode foundry.utils.mergeObject} * also expands the object. So once `ExpandsTo` exists you should also use * that helper type. * * What these patterns have in common is that if `options` looks like * `{ prop: undefined }` that will override whatever is in `defaultOptions` * and may cause issues. Note that even if you see one of these patterns you * also need to ensure that `undefined` would cause issues down the road * before using `IntentionalPartial` as it could be an intended way of * resetting a property. * - Use `InexactPartial` when `null` is problematic but `undefined` is not. * The most common time this shows up is with the pattern * `exampleFunction({ prop = "foo" } = {}) { ... }`. * * @internal */ export type InexactPartial<T extends object> = { [K in keyof T]?: T[K] | undefined; }; /** * Makes select properties in `T` optional and explicitly allows both `null` and * `undefined` values. * * ### Picking the right helper type * - Favor `NullishProps` whenever it is valid. Allowing both `null` and * `undefined` is convenient for the end user and it is very common that * wherever `undefined` is valid so is `null`. For some examples it is valid * for `options.prop ??= "default"`, `options.prop ||= "default"`, * `if (options.prop) { ... }`, `if (options.prop == null)`, or so on. * - Use `IntentionalPartial` when an explicit `undefined` is problematic but * leaving off the property entirely is fine. This primarily occurs when * patterns like `options = { ...defaultOptions, ...options }`, * `Object.assign({}, defaultOptions, options)`, * `foundry.utils.mergeObject(defaultOptions, options)`, or so on. * * Note that {@linkcode foundry.utils.mergeObject} * also expands the object. So once `ExpandsTo` exists you should also use * that helper type. * * What these patterns have in common is that if `options` looks like * `{ prop: undefined }` that will override whatever is in `defaultOptions` * and may cause issues. Note that even if you see one of these patterns you * also need to ensure that `undefined` would cause issues down the road * before using `IntentionalPartial` as it could be an intended way of * resetting a property. * - Use `InexactPartial` when `null` is problematic but `undefined` is not. * The most common time this shows up is with the pattern * `exampleFunction({ prop = "foo" } = {}) { ... }`. * * @internal */ export type NullishProps<T extends object> = { [K in keyof T]?: T[K] | null | undefined; }; /** * Expand an object that contains keys in dotted notation * @internal */ export type Expanded<O> = O extends AnyObject ? { [KO in keyof O as KO extends `${infer A}.${string}` ? A : KO]: KO extends `${string}.${infer B}` ? Expanded<{ [EB in B]: O[KO] }> : Expanded<O[KO]>; } : O; /** * Union type of the types of the values in `T` * @internal */ export type ValueOf<T extends object> = T extends ReadonlyArray<infer V> ? V : T[keyof T]; type OmitIndex<K extends PropertyKey> = string extends K ? never : number extends K ? never : symbol extends K ? never : K; /** * Gets the keys of `T` but excluding index signatures unlike `keyof T`. For example `Record<string, any> & { foo: number }` will produce `string` with `keyof` but `foo` with `ConcreteKeys`. */ export type ConcreteKeys<T> = [T] extends [never] ? never : keyof { [K in keyof T as OmitIndex<K>]: never; }; /** * Removes all index signatures from an object. Use this instead of `[K in keyof ConcreteKeys<T>]` to preserve modifiers e.g. readonly, or optional. */ // NOTE(LukeAbby): It may seem easier to write `Pick<T, ConcreteKeys<T>>` but this stops it from being a homomorphic mapped type and regresses its power when given a generic type parameter or `this`. // See: https://www.typescriptlang.org/play/?#code/KYDwDg9gTgLgBDAnmYcDCEB2BjKwbADSwiAzgDwAqAfHALxwDWJEAZnAN4BQccA2oTgBLTExbtKcAIak4pGFBEBzOKAKYAJrMEB+OJmAA3YFDgAufQFcAtgCMTqkOq1xd+ow4ulEdiABtHZ204PQNjUwtCAF0LMJMAbi4AX0SuJBQ4ACVgawhjAElNUABlISVMKRhLPAoaejgABSFsRioAGnQsXHwiElrqalTsPxlZABFKqQBZCA1gP3Ji7AALHKlabl454ak8OFy5vwts3IKikFLyyurgCiXV63Xkri4d0lliy1sZw8WVtcCwE0sg4cCUQJMzQaUAgYAsUkwiHi-EIXgUyhiVjsDiStDUQJcExg01m8z+D3WnB4+3wy1mAAoAJRU3i8AD0bLglGWQlkYBhKCgiDkdMsfg0jl58FslngGggt0wAHIYAA6am8GA80iqg7zVXggyKbDQ2GpVkIbW60l+VVSWzYRK8JLJIA export type RemoveIndexSignatures<T extends object> = { [K in keyof T as OmitIndex<K>]: T[K]; }; /** * Transforms a string to lowercase and the first character to uppercase. * @internal */ export type Titlecase<S extends string> = S extends `${infer A} ${infer B}` ? `${Titlecase<A>} ${Titlecase<B>}` : Capitalize<Lowercase<S>>; /** * Deeply merge two types. If either of the given types is not an object then `U` * simply overwrites `T`. * * Nested properties of type `object` are merged recursively unless the property * in `U` is an `Array`. * * @template T - The base type that `U` will be merged into. * @template U - The type that will be merged into `T`. */ export type Merge<T, U> = U extends object ? (T extends object ? _Merge<T, U> : U) : U; type _Merge<T extends object, U extends object> = T extends AnyArray ? U extends AnyArray ? MergeArray<T, U> : _MergeObject<T, U> : _MergeObject<T, U>; type _MergeObject<T extends object, U extends object> = U extends AnyObject ? T extends AnyObject ? _MergePlainObject<T, U> : _MergeComplexObject<T, U> : _MergeComplexObject<T, U>; type MergeArray<T extends AnyArray, U extends AnyArray> = number extends U["length"] | T["length"] ? Array<T[number] | U[number]> : [...U, ...DropFirstN<T, U["length"]>]; type DropFirstN< T extends AnyArray, DropN extends number, Accumulator extends AnyArray = [], > = Accumulator["length"] extends DropN ? T : T extends [unknown, ...infer Items] ? DropFirstN<Items, DropN, [...Accumulator, 1]> : []; // TODO(LukeAbby): This needs to be more complex as to account for stuff like optionality correctly. type _MergePlainObject<T extends object, U extends object> = { [K in keyof T as K extends keyof U ? never : K]: T[K]; } & { [K in keyof U]: T extends { readonly [_ in K]?: infer V } ? Merge<V, U[K]> : U[K]; }; interface _MergeComplexObject<T extends object, U extends object> extends _Override<T, _MergePlainObject<T, U>> {} /** * Overrides properties of `T` with properties in `U`. Be careful using this type as its internal * implementation is likely a bit shaky. * * Note: `U` must NOT be a union. If it is unexpected behavior may occur. */ export type Override<T extends object, U extends object> = T extends unknown ? _Override<T, U> : never; // @ts-expect-error This pattern is inherently an error. interface _Override<T extends object, U extends object> extends U, T {} /** * Returns whether the type is a plain object. Excludes functions, arrays, and constructors while still being friendly to interfaces. * * @example * ```ts * interface ObjectInterface { * prop: number; * } * * type Interface = IsObject<ObjectInterface>; // true * type Object = IsObject<{ prop: number }>; // true * type Array = IsObject<number[]>; // false * type Function = IsObject<() => void>; // false * * // By comparison, simply comparing against `Record<string, unknown>` fails. * type RecordFails = Interface extends Record<string, unknown> ? true : false; // false * ``` */ export type IsObject<T> = T extends object ? (T extends AnyArray | AnyFunction | AnyConstructor ? false : true) : false; /** * A simple, non-recursive merge type. * @template Target - the target type to merge into * @template Override - the type whose properties override the ones in Target */ export type SimpleMerge<Target, Override> = Omit<Target, keyof Override> & Override; /** * Makes the given keys `K` of the type `T` required */ export type RequiredProps<T extends object, K extends AllKeysOf<T>> = Required<Pick<T, K>> & Omit<T, K>; export type Mixin<MixinClass extends AnyConcreteConstructor, BaseClass extends AnyConstructor> = MixinClass & BaseClass; interface GetDataConfigOptions<T> { partial: Partial<T> & Record<string, unknown>; exact: T; object: object; } type GetDataConfigOption = GetDataConfig extends { mode: infer Mode extends keyof GetDataConfigOptions<unknown>; } ? Mode : "object"; export type GetDataReturnType<T extends object> = GetDataConfigOptions<T>[GetDataConfigOption]; /** * Replaces the type `{}` with `Record<string, never>` by default which is * usually a better representation of an empty object. The type `{}` actually * allows any type be assigned to it except for `null` and `undefined`. * * The theory behind this is that all non-nullish types allow * you to access any property on them without erroring. Primitive types like * `number` will not store the property but it still will not error to simply * try to get and set properties. * * The type `{}` can appear for example after operations like `Omit` if it * removes all properties rom an object, because an empty interface was given, * or so on. * * @example * ```ts * type ObjectArray<T extends Record<string, unknown>> = T[]; * * // As you would hope a string can't be assigned in a union or not. It errors with: * // "type 'string' is not assignable to type 'Record<string, unknown>'." * type UnionErrors = ObjectArray<string | { x: number }>; * * // However, this works. * type EmptyObjectArray = ObjectArray<{}>; * * // But it allows likely unsound behavior like this: * const emptyObject: EmptyObjectArray = [1, "foo", () => 3]; * * // So it may be better to define `ObjectArray` like so: * type ObjectArray<T extends Record<string, unknown>> = HandleEmptyObject<T>[]; * * // If it were, then this line would error appropriately! * const emptyObject: EmptyObjectArray = [1, "foo", () => 3]; * ``` */ export type HandleEmptyObject<O, D = EmptyObject> = // Note(LukeAbby): This uses a strict equality test to differentiate types like `{ onlyOptional?: true }` // and `object` from `{}`. More naive tests like `[{}] extends [O] ? ... : ...` fails these cases // due to particular unsoundness rules around `{}`. // // eslint-disable-next-line @typescript-eslint/no-empty-object-type (<T>() => T extends {} ? true : false) extends <T>() => T extends O ? true : false ? D : O; /** * This type allows any plain objects. In other words it disallows functions * and arrays. * * Use this type instead of: * - `object`/`Record<any, any>` - This allows functions, classes, and arrays. * - `{}` - This type allows anything besides `null` and `undefined`. * - `Record<string, unknown>` - This is the appropriate type for any mutable object but doesn't allow readonly objects. */ // This type is not meant to be extended and it has to use an indexed type. // eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type AnyObject = { readonly [K: string]: unknown; }; /** * This type allows mutable plain objects. This means readonly objects cannot be * assigned. * * Use this type instead of: * - `object` - This allows functions and arrays. * - `Record<string, any>`/`{}` - These allows anything besides `null` and `undefined`. * - `Record<string, unknown>` - These types are equivalent but `AnyMutableObject` is preferred for explicitness. */ // eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type AnyMutableObject = { [K: string]: unknown; }; /** * Use this type to allow any array. This allows readonly arrays which is * generally what you want. If you need a mutable array use the * {@linkcode MutableArray} type instead of the builtin `T[]` or * `Array` types. This allows us to be more explicit about intent. * * Consider being more specific if possible. You should generally try to use a * concrete array with a union or add a type parameter first. * * Use this instead of: * - `any[]` - When elements of this array are accessed you get `any` which is not safe. * - `unknown[]` - This is the appropriate type for any mutable array but doesn't allow readonly arrays. */ export type AnyArray = readonly unknown[]; /** * Use this type to allow a mutable array of type `T`. Only use this if the * array can be soundly mutated. Otherwise you should be using * `readonly T[]` or {@linkcode ReadonlyArray} */ export type MutableArray<T> = Array<T>; /** * Use this type to allow any function. Notably since this allows any function * it is difficult to call the function in a safe way. This means its uses are * mostly niche and it should be avoided. * * Make sure you have a good reason to use this type. It is almost always better * to use a more specific function type. Please consider leaving a comment about * why this type is necessary. * * Use this instead of: * - `Function` - This refers to the fundamental `Function` object in JS. It allows classes. * - `(...args: any[]) => any` - If someone explicitly accesses the parameters or uses the return value you get `any` which is not safe. * - `(...args: unknown[]) => unknown` - This allows obviously unsound calls like `fn(1, "foo")` because it indicates it can take any arguments. */ export type AnyFunction = (...args: never) => unknown; /** * Use this type to allow any class, abstract class, or class-like constructor. * * See {@linkcode AnyConcreteConstructor} if you cannot * allow abstract classes. Please also consider writing a comment * explaining why {@linkcode AnyConcreteConstructor} is * necessary. * * @example * ```ts * const concrete: AnyConstructor = class Concrete { ... } * const abstract: AnyConstructor = abstract class Abstract { ... } * * // `Date` is not actually a class but it can be used as a constructor. * const classLike: AnyConstructor = Date; * ``` */ export type AnyConstructor = abstract new (...args: never) => unknown; /** * Use this type to allow any class or class-like constructor but disallow * class-like constructors. * * Use this type only when abstract classes would be problematic such as the * base type of a mixin. Please consider writing a comment explaining why. * See {@linkcode AnyConstructor} to also allow abstract classes. * * @example * ```ts * const concrete: AnyConcreteConstructor = class Concrete { ... } * * // `Date` is not actually a class but it can be used as a constructor. * const classLike: AnyConcreteConstructor = Date; * * // This next line errors: * const abstract: AnyConcreteConstructor = abstract class Abstract { ... } * ``` */ export type AnyConcreteConstructor = new (...args: never) => unknown; /** * This type is equivalent to `Promise<T>` but exists to give an explicit signal * that this is not a mistake. When Foundry accepts an asynchronous callback the * vast majority of the time it is best to use {@linkcode MaybePromise}. * * By doing it this way the maximum flexibility is given to the definer of the * callback. This is okay because typically asynchronous callbacks are simply * awaited, meaning that there's no noticeable difference between a `Promise` * and {@linkcode MaybePromise}. Even functions like * {@linkcode Promise.allSettled} function correctly * with {@linkcode MaybePromise}. * * Do not use this type or {@linkcode MaybePromise} for the return * type of asynchronous methods on classes. For example for * {@link foundry.abstract.Document._preCreate | `Document#_preCreate`} the typing * should be `Promise<void>` and not this type. In theory we could use * {@linkcode MaybePromise} in this context as well but this seems * more likely to be confusing than to be helpful. * * Use this type only in the rare case where a callback's return type must be a * `Promise`, for example if `promise.then` or `promise.catch` is explicitly * called. Please also writing a comment explaining why * {@linkcode MaybePromise} is problematic in this context. */ export type MustBePromise<T> = Promise<T>; /** * Use when a type may be either a promise or not. This is most useful in * asynchronous callbacks where in most cases it's sound to provide a synchronous * callback instead. * * If it is not sound to provide a non-Promise for whatever reason, see * {@linkcode MustBePromise} to declare this more explicitly than simply writing * `Promise<T>`. * * This should generally not be used in asynchronous methods. For example in * {@link foundry.abstract.Document._preCreate | `Document#_preCreate`} the typing * is `Promise<void>` because it's declared as an async method. Overriding an * asynchronous method with a synchronous method is more confusing than * helpful. */ export type MaybePromise<T> = T | Promise<T>; /** * Use this to allow any type besides `null` or `undefined`. * * This type is equivalent to the type `{}`. It exists to give this type a * better name. `{}` is not a type representing an empty object. In reality it * allows assigning any type besides `null` or `undefined`. This is frustrating * but it seems the theory is supposed to be that all types except for `null` * and `undefined` will return `undefined` for any property accessed on them. * * Even primitives like `number` will not error when you get or even set a * property on them, although they will not preserve the property. Since the * only type that cannot be indexed is `null` or `undefined` this is the chosen * semantics of `{}` in TypeScript. */ // This type is not meant to be extended and it's meant to be the explicit version of what the type `{}` does, i.e. allow any type besides `null` or `undefined`. // eslint-disable-next-line @typescript-eslint/consistent-type-definitions, @typescript-eslint/no-empty-object-type export type NonNullish = {}; /** * This is the closest approximation to a type representing an empty object. * * Use instead of `{}` when you want to represent an empty object. `{}` actually * allows any type that is not `null` or `undefined`. see * {@linkcode NonNullish} if you want that behavior. */ // It would be unsound to merge into so an interface is not used. export type EmptyObject = Record<string, never>; /** * This helper type helps emulate index signatures for types with incompatible concrete keys. * * For example the type `{ foo: number; [K: string]: string }` is not allowed because the index signature is incompatible with the concrete key `foo`. * It may seem like tricks like `{ foo: number; } & { [K: string]: string }` work but this defers the error to the caller. * * @example * ```typescript * type NaiveType = { foo: number; [K: string]: boolean }; * // ^ Property 'foo' of type 'number' is not assignable to 'string' index type 'boolean'. * * type NaiveIntersection = { foo: number } & { [K: string]: string }; * * function usesIntersection(intersection: NaiveIntersection) { ... } * * usesIntersection({ foo: 1, x: true }); * // ^ Argument of type '{ foo: number; }' is not assignable to parameter of type 'NaiveIntersection'. * // Type '{ foo: number; }' is not assignable to type '{ [K: string]: boolean; }'. * // Property 'foo' is incompatible with index signature. * // Type 'number' is not assignable to type 'boolean'. * * function usesCorrectly<T extends AnyObject>(withIndex: ShapeWithIndexSignature<T, { foo: number }, string, boolean>) { ... } * * usesCorrectly({ foo: 1, x: true }); * ``` */ // Note: If https://github.com/microsoft/TypeScript/issues/17867 or https://github.com/microsoft/TypeScript/issues/43826 (depending on implementation) is implemented it's likely this type can be expressed in a better way. export type ShapeWithIndexSignature< T extends AnyObject, // Uses `extends object` to allow interfaces and if useful other objects. PrimaryShape extends object, IndexSignature extends PropertyKey, IndexType, > = PrimaryShape & { readonly [K in keyof T & IndexSignature]: K extends keyof PrimaryShape ? PrimaryShape[K] : IndexType; }; export type MustBeValidUuid<Uuid extends string, Type extends Document.Type = Document.Type> = _MustBeValidUuid< Uuid, Uuid, Type >; /** * Quotes a string for human readability. This is useful for error messages. * * @example * ```ts * type Quote1 = Quote<"foo">; * // ^ "'foo'" * * type Quote2 = Quote<"can't">; * // ^ "'can\\'t'" * ``` */ export type Quote<T extends string> = T extends `${string}'${string}` ? `'${Escape<T>}'` : `'${T}'`; type Escape<T extends string> = T extends `${infer Prefix}'${infer Suffix}` ? `${Prefix}\\'${Escape<Suffix>}` : T; declare class InvalidUuid<OriginalUuid extends string> { #invalidUuid: true; message: `The UUID ${Quote<OriginalUuid>} is invalid .`; } type _MustBeValidUuid< Uuid extends string, OriginalUuid extends string, Type extends Document.Type, > = Uuid extends `${string}.${string}.${infer Rest}` ? _MustBeValidUuid<Rest, OriginalUuid, Type> : Uuid extends `${string}.${string}` ? Uuid extends `${Type}.${string}` ? OriginalUuid : InvalidUuid<OriginalUuid> : `${Type}.${string}` | `${string}.${string}.${Type}.${string}`; /** * This type is used when you want to use `unknown` in a union. This works because while `T | unknown` * will reduce to `unknown`. However by comparison the only way that `T | LazyUnknown` can reduce is * if there's * * This makes it the ideal type to accept any input in some situations, mostly for documenting types * where anything is acceptable but certain ones are more notable. For example `number | LazyUnknown` * would still accept anything but appear as `number | {} | null | undefined` in intellisense, due * to the inlining of the composite parts of `LazyUnknown`. * * The type `{}` isn't actually the type for an empty object. It allows anything except * `null`/`undefined` which is why `{} | null | undefined` allows anything to be assigned to it. * See {@linkcode NonNullish} for a further explanation on why `{}` allows anything * besides `null`/`undefined`. */ export type LazyUnknown = NonNullish | null | undefined; /** * `Coalesce` is useful to provide defaults. For example if you have the function: * ```js * function toString(item="default") { * return `${item}`; * } * ``` * * Then the most appropriate type might be: * ```ts * declare function toString< * Item extends string | number | undefined = undefined * >(item?: Item): `${Coalesce<Item, "default">}` * * const itemUnset = toString(); * // ^ "default" * * const itemUndefined = tgoString(undefined); * // ^ "default" * ``` * * This is because generic parameter defaults and function parameters behave differently. A function * default is used whenever `undefined` would otherwise be the value, either explicitly or implicitly * i.e. both `doubles()` and `doubles(undefined)` would use the default of `"default"`. However a generic' * defaults only shows up when it's not used at all. The behavior with an explicit `undefined` has * two cases to explain: * * ```ts * declare function toString1<Item extends string | number = "foo">(item?: Item): void; * * toString1(undefined); * // `Item` will infer as `string | number` because `undefined` isn't assignable to the constraint of `Item`. * * declare function toString2<Item extends string | number | undefined = "foo">(item?: Item): void; * * toString2(undefined); * // `Item` will infer as `undefined` not `"foo"` because the generic has something to infer from. * ``` */ export type Coalesce<T, D, CoalesceType = undefined> = T extends CoalesceType ? D : T; /** * Coalesces specifically `null | undefined`. Behaves like `??` does at runtime. * See {@linkcode Coalesce}. */ export type NullishCoalesce<T, D> = T extends null | undefined ? D : T; export interface EarlierHook { none: never; init: "none"; i18nInit: "none" | "init"; setup: "none" | "init" | "i18nInit"; ready: "none" | "init" | "i18nInit" | "setup"; } /** * A hook that's valid to use in {@linkcode AssumeHookRan} */ export type InitializationHook = keyof EarlierHook; /** * All hooks ran so far. * * @example * ```ts * HooksRan<never>; // never * HooksRan<"none">; // "none" * HooksRan<"init">; // "none" | "init" * HooksRan<"i18nInit">; // "none" | "init" | "i18nInit" * HooksRan<"setup">; // "none" | "init" | "i18nInit" | "setup" * HooksRan<"ready">; // "none" | "init" | "i18nInit" | "setup" | "ready" * ``` */ export type HooksRan<T extends InitializationHook> = EarlierHook[T] | T; type ValidHooksRan = Extract<keyof AssumeHookRan, InitializationHook>; /** * Various things within Foundry are only initialized after a certain hook is ran. * `InitializedOn` is useful to help model this pattern. */ export type InitializedOn< InitializedTo, InitializedOnHook extends InitializationHook, BeforeInitialization = InitializedTo | undefined, > = [InitializedOnHook] extends [HooksRan<ValidHooksRan>] ? InitializedTo : BeforeInitialization; /** * Returns the inputted type. This may seem like a useless type but it exists due to limitations * with the syntax of TypeScript interfaces. For example it's a syntax error to write * `interface Example extends typeof SomeClass {}` but `typeof SomeClass` is a reasonable base type * so to work around this you can write `interface Example extends Identity<typeof SomeClass> {}` */ export type Identity<T extends object> = T;