import type { ConstructorOverloadParameters, NumOverloads, OverloadsInfoUnion } from './overloads'; import type { IsNever, IsAny, IsUnknown, ReadonlyKeys, RequiredKeys, OptionalKeys, MutuallyExtends, UnionToTuple, IsTuple, UnionToIntersection, TupleToRecord, IsRecord } from './utils'; export type DeepBrandOptions = { nominalTypes: {}; }; export type DeepBrandOptionsDefaults = { nominalTypes: { Date: Date; }; }; export type NominalType = Options['nominalTypes'] extends infer N ? { [K in keyof N]: MutuallyExtends extends true ? K : never; }[keyof N] : never; /** * Represents a deeply branded type. * * Recursively walk a type and replace it with a branded type related to the * original. This is useful for equality-checking stricter than * `A extends B ? B extends A ? true : false : false`, because it detects the * difference between a few edge-case types that vanilla TypeScript * doesn't by default: * - `any` vs `unknown` * - `{ readonly a: string }` vs `{ a: string }` * - `{ a?: string }` vs `{ a: string | undefined }` * * __Note__: not very performant for complex types - this should only be used * when you know you need it. If doing an equality check, it's almost always * better to use {@linkcode StrictEqualUsingTSInternalIdenticalToOperator}. */ export type DeepBrand = IsNever extends true ? { type: 'never'; } : IsAny extends true ? { type: 'any'; } : IsUnknown extends true ? { type: 'unknown'; } : NominalType extends infer Nominal ? IsNever extends true ? T extends string | number | boolean | symbol | bigint | null | undefined | void ? { type: 'primitive'; value: T; } : T extends new (...args: any[]) => any ? { type: 'constructor'; params: ConstructorOverloadParameters; instance: DeepBrand any>>, Options>; } : T extends (...args: infer P) => infer R ? NumOverloads extends 1 ? { type: 'function'; params: DeepBrand; return: DeepBrand; this: DeepBrand, Options>; props: DeepBrand, Options>; } : UnionToTuple> extends infer OverloadsTuple ? { type: 'overloads'; overloads: { [K in keyof OverloadsTuple]: DeepBrand; }; } : never : T extends any[] ? IsTuple extends true ? { type: 'tuple'; items: { [K in keyof T]: DeepBrand; }; } : { type: 'array'; items: DeepBrand; } : IsRecord extends true ? { type: 'record'; keys: keyof T; values: DeepBrand; } : { type: 'object'; properties: { [K in keyof T]: DeepBrand; }; readonly: ReadonlyKeys; required: RequiredKeys; optional: OptionalKeys; constructorParams: ConstructorOverloadParameters extends infer P ? IsNever

extends true ? never : DeepBrand : never; } : { type: Nominal; } : never; /** * Checks if two types are strictly equal using branding. */ export type StrictEqualUsingBranding = MutuallyExtends, DeepBrand>; /** * @internal don't use this unless you are deeply familiar with it! * * Walks over a type `T`, assuming that it's the output of the {@linkcode DeepBrand} utility. It looks for leaf nodes looking like `{type: FindType}`. * When it finds them, it merges them into a string->string record, keeping track of a rough representation of the path-location. * For simple objects, this path will roughly match dot-prop notation but it also traverses into all the structures that `DeepBrand` can emit. * But it also goes into overloads, function parameters, return types, etc. The output is an ugly intersection of objects along with a marker `{deepBrandLeafNode: true}` * which is purely for internal use. The output should not be shown to end-users! */ type _DeepPropTypesOfBranded = IsNever extends true ? {} : T extends string ? {} : T extends { type: FindType; } ? { [K in PathTo]: T['type']; } & { deepBrandLeafNode: true; } : T extends any[] ? _DeepPropTypesOfBranded, PathTo, FindType> : UnionToIntersection<{ [K in keyof T]: Extract<_DeepPropTypesOfBranded>}`, FindType>, { deepBrandLeafNode: true; }>; }[keyof T]>; /** Required options for for {@linkcode DeepBrandPropNotes}. */ export type DeepBrandPropNotesOptions = Partial & { findType: 'any' | 'never' | 'unknown'; }; /** Default options for for {@linkcode DeepBrandPropNotes}. */ export type DeepBrandPropNotesOptionsDefaults = { findType: 'any' | 'never'; }; /** * For an input type `T`, finds all deeply-nested properties in the {@linkcode DeepBrand} representation of it. * * The output is a developer-readable shallow record of prop-path -> resolved type. * @example * ```ts * type X = {a: any; b: boolean; c: {d: any}} * const notes: DeepBrandPropNotes = { * '.a': 'any', * '.c.d': 'any', * } * ``` */ export type DeepBrandPropNotes = _DeepPropTypesOfBranded, '', Options['findType']> extends infer X ? {} extends X ? Record : { [K in Exclude]: X[K]; } : never; /** * @internal * * Helper to coerce a type `K` that you are already pretty sure is a string because it camed from a `keyof T` type expression. * Useful because sometimes TypeScript forgets that. * When it's not a string or number, it will output a big ugly literal type `'UNEXPECTED_NON_LITERAL_PROP'` - try to avoid this! */ export type Prop = K extends string | number ? K : 'UNEXPECTED_NON_LITERAL_PROP'; /** * Gets a sensible suffix to a property path for a {@linkcode DeepBrand} output type. * `[number]` for arrays, empty string for objects, and parenthesised-input for anything else. */ type DeepBrandPropPathSuffix = T extends { type: string; } ? K extends 'items' ? '[number]' : K extends 'properties' ? '' : `(${Prop})` : `.${Prop}`; export {};