UNPKG

expect-type

Version:

[![CI](https://github.com/mmkal/expect-type/actions/workflows/ci.yml/badge.svg)](https://github.com/mmkal/expect-type/actions/workflows/ci.yml) ![npm](https://img.shields.io/npm/dt/expect-type) [![X (formerly Twitter) Follow](https://img.shields.io/twitte

62 lines (61 loc) 2.4 kB
import type { ConstructorOverloadParameters, NumOverloads, OverloadsInfoUnion } from './overloads'; import type { IsNever, IsAny, IsUnknown, ReadonlyKeys, RequiredKeys, OptionalKeys, MutuallyExtends, UnionToTuple } from './utils'; /** * 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<T> = IsNever<T> extends true ? { type: 'never'; } : IsAny<T> extends true ? { type: 'any'; } : IsUnknown<T> extends true ? { type: 'unknown'; } : T extends string | number | boolean | symbol | bigint | null | undefined | void ? { type: 'primitive'; value: T; } : T extends new (...args: any[]) => any ? { type: 'constructor'; params: ConstructorOverloadParameters<T>; instance: DeepBrand<InstanceType<Extract<T, new (...args: any) => any>>>; } : T extends (...args: infer P) => infer R ? NumOverloads<T> extends 1 ? { type: 'function'; params: DeepBrand<P>; return: DeepBrand<R>; this: DeepBrand<ThisParameterType<T>>; props: DeepBrand<Omit<T, keyof Function>>; } : UnionToTuple<OverloadsInfoUnion<T>> extends infer OverloadsTuple ? { type: 'overloads'; overloads: { [K in keyof OverloadsTuple]: DeepBrand<OverloadsTuple[K]>; }; } : never : T extends any[] ? { type: 'array'; items: { [K in keyof T]: T[K]; }; } : { type: 'object'; properties: { [K in keyof T]: DeepBrand<T[K]>; }; readonly: ReadonlyKeys<T>; required: RequiredKeys<T>; optional: OptionalKeys<T>; constructorParams: DeepBrand<ConstructorOverloadParameters<T>>; }; /** * Checks if two types are strictly equal using branding. */ export type StrictEqualUsingBranding<Left, Right> = MutuallyExtends<DeepBrand<Left>, DeepBrand<Right>>;