diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c3b3dc2..a9a07b41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,120 @@ # Changelog +## v1.21.0 + +[compare changes](https://github.com/ashgw/ts-roids/compare/v1.17.0...v1.21.0) + +### 🚀 Enhancements + +- **#52:** Finish `ImmutableKeys` & `MutableKeys` ([17482f9](https://github.com/ashgw/ts-roids/commit/17482f9)) +- Set `IfExtends` ([a1625d0](https://github.com/ashgw/ts-roids/commit/a1625d0)) +- **#54:** Finish `DeepOmit` ([65ab454](https://github.com/ashgw/ts-roids/commit/65ab454)) + +### 🏡 Chore + +- **release:** V1.18.0 ([68f1d2d](https://github.com/ashgw/ts-roids/commit/68f1d2d)) +- Cleanup source file ([fd40124](https://github.com/ashgw/ts-roids/commit/fd40124)) +- Cleanup ([e384ba9](https://github.com/ashgw/ts-roids/commit/e384ba9)) +- Cleanup ([33e7560](https://github.com/ashgw/ts-roids/commit/33e7560)) + +### ❤️ Contributors + +- AshGw ([@AshGw](http://github.com/AshGw)) + +## v1.20.1 + +[compare changes](https://github.com/ashgw/ts-roids/compare/v1.17.0...v1.20.1) + +### 🚀 Enhancements + +- **#52:** Finish `ImmutableKeys` & `MutableKeys` ([17482f9](https://github.com/ashgw/ts-roids/commit/17482f9)) +- Set `IfExtends` ([a1625d0](https://github.com/ashgw/ts-roids/commit/a1625d0)) + +### 🏡 Chore + +- **release:** V1.18.0 ([68f1d2d](https://github.com/ashgw/ts-roids/commit/68f1d2d)) +- Cleanup source file ([fd40124](https://github.com/ashgw/ts-roids/commit/fd40124)) +- Cleanup ([e384ba9](https://github.com/ashgw/ts-roids/commit/e384ba9)) + +### ❤️ Contributors + +- AshGw ([@AshGw](http://github.com/AshGw)) + +## v1.20.0 + +[compare changes](https://github.com/ashgw/ts-roids/compare/v1.17.0...v1.20.0) + +### 🚀 Enhancements + +- **#52:** Finish `ImmutableKeys` & `MutableKeys` ([17482f9](https://github.com/ashgw/ts-roids/commit/17482f9)) +- Set `IfExtends` ([a1625d0](https://github.com/ashgw/ts-roids/commit/a1625d0)) + +### 🏡 Chore + +- **release:** V1.18.0 ([68f1d2d](https://github.com/ashgw/ts-roids/commit/68f1d2d)) +- Cleanup source file ([fd40124](https://github.com/ashgw/ts-roids/commit/fd40124)) +- Cleanup ([e384ba9](https://github.com/ashgw/ts-roids/commit/e384ba9)) + +### ❤️ Contributors + +- AshGw ([@AshGw](http://github.com/AshGw)) + +## v1.19.1 + +[compare changes](https://github.com/ashgw/ts-roids/compare/v1.17.0...v1.19.1) + +### 🚀 Enhancements + +- **#52:** Finish `ImmutableKeys` & `MutableKeys` ([17482f9](https://github.com/ashgw/ts-roids/commit/17482f9)) +- Set `IfExtends` ([a1625d0](https://github.com/ashgw/ts-roids/commit/a1625d0)) + +### 🏡 Chore + +- **release:** V1.18.0 ([68f1d2d](https://github.com/ashgw/ts-roids/commit/68f1d2d)) +- Cleanup source file ([fd40124](https://github.com/ashgw/ts-roids/commit/fd40124)) +- Cleanup ([e384ba9](https://github.com/ashgw/ts-roids/commit/e384ba9)) + +### ❤️ Contributors + +- AshGw ([@AshGw](http://github.com/AshGw)) + +## v1.19.0 + +[compare changes](https://github.com/ashgw/ts-roids/compare/v1.17.0...v1.19.0) + +### 🚀 Enhancements + +- **#52:** Finish `ImmutableKeys` & `MutableKeys` ([17482f9](https://github.com/ashgw/ts-roids/commit/17482f9)) +- Set `IfExtends` ([a1625d0](https://github.com/ashgw/ts-roids/commit/a1625d0)) + +### 🏡 Chore + +- **release:** V1.18.0 ([68f1d2d](https://github.com/ashgw/ts-roids/commit/68f1d2d)) +- Cleanup source file ([fd40124](https://github.com/ashgw/ts-roids/commit/fd40124)) +- Cleanup ([e384ba9](https://github.com/ashgw/ts-roids/commit/e384ba9)) + +### ❤️ Contributors + +- AshGw ([@AshGw](http://github.com/AshGw)) + +## v1.18.0 + +[compare changes](https://github.com/ashgw/ts-roids/compare/v1.16.0...v1.18.0) + +### 🚀 Enhancements + +- **#50:** Finish `UniqueArray` ([d713ccc](https://github.com/ashgw/ts-roids/commit/d713ccc)) +- **#52:** Finish `ImmutableKeys` & `MutableKeys` ([cebb07e](https://github.com/ashgw/ts-roids/commit/cebb07e)) + +### 🏡 Chore + +- **release:** V1.17.0 ([71399b1](https://github.com/ashgw/ts-roids/commit/71399b1)) + +### ❤️ Contributors + +- AshGw ([@AshGw](http://github.com/AshGw)) + ## v1.17.0 [compare changes](https://github.com/ashgw/ts-roids/compare/v1.16.0...v1.17.0) diff --git a/README.md b/README.md index 0a5d91a2..3e998476 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ ### Installation ```bash npm i ts-roids +# or +pnpm i ts-roids ``` - +If you're only using types, you can install it as a dev dependency. ### Examples #### Lock a class (make it immutable) and finalize it (prohibit further extension) ```ts diff --git a/package.json b/package.json index d42ebe07..69a21d7b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ts-roids", - "version": "1.17.0", + "version": "1.21.0", "private": false, "description": "Extending the TS library with types and decorators that should've been built-in", "keywords": [ diff --git a/src/index.ts b/src/index.ts index 8f8804e9..116bd504 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,12 +1,12 @@ /** - * `Nullable` type allows representing a value that can be either null or undefined. - * @type {Nullable} + * `Nullable` export type allows representing a value that can be either null or undefined. + * @export type {Nullable} */ export type Nullable = null | undefined; /** - * Represents a type that can hold any numeric value: number or a bigint. - * @type {Numeric} + * Represents a export type that can hold any numeric value: number or a bigint. + * @export type {Numeric} */ export type Numeric = number | bigint; @@ -17,176 +17,205 @@ export type Numeric = number | bigint; * - `string`: Represents textual data. * - `boolean`: Represents a logical value (true or false). * - `symbol`: Represents a unique and immutable value. - * @type {Primitive} + * @export type {Primitive} */ export type Primitive = string | boolean | symbol | Nullable | Numeric; /** - * Represents a type that includes falsy values in JavaScript. + * Represents a export type that includes falsy values in JavaScript. * Falsy values are those that coerce to false when used in a boolean context. * This includes `false`, an empty string (`''`), numeric zero (`0`), `null`, * and `undefined`. - * @type {Falsy} + * @export type {Falsy} */ export type Falsy = false | '' | 0 | Nullable; /** - * Checks if a given type `T` is `Falsy`. + * Checks if a given export type `T` is `Falsy`. * @returns `true` if `T` is a subtype of `Falsy`, otherwise `false`. * @example - * type Falsy = IsFalsy<''>; // TestFalsy is tru` - * type Truthy = IsFalsy<10>; // TestTruthy is false + * export type Falsy = IsFalsy<''>; // TestFalsy is tru` + * export type Truthy = IsFalsy<10>; // TestTruthy is false */ export type IsFalsy = T extends Falsy ? true : false; /** - * Checks if a given type `T` is a truthy value. + * Checks if a given export type `T` is a truthy value. * A truthy value is any value that is not a falsy value. * @returns `true` if `T` is not a subtype of `Falsy`, otherwise `false`. * @example - * type TruthyString = IsTruthy; // => true - * type TruthyNumber = IsTruthy<10>; // => true - * type FalsyNull = IsTruthy; // => false - * type FalsyEmptyString = IsTruthy<''>; => false + * export type TruthyString = IsTruthy; // => true + * export type TruthyNumber = IsTruthy<10>; // => true + * export type FalsyNull = IsTruthy; // => false + * export type FalsyEmptyString = IsTruthy<''>; => false */ export type IsTruthy = T extends Exclude ? true : false; /** - * Checks if a given type `T` is `never`. - * The `never` type represents a value that never occurs, + * Checks if a given export type `T` is `never`. + * The `never` export type represents a value that never occurs, * for example a function that always errors out. * @returns `true` if `T` is `never`, otherwise `false`. * @example - * type Never = IsNever; => true - * type NotNever = IsNever; => false + * export type Never = IsNever; => true + * export type NotNever = IsNever; => false */ export type IsNever = T extends never ? true : false; /** - * Checks if a given type `T` is `unknown`. - * The `unknown` type is the type-safe counterpart of `any`. - * Values of type `unknown` can hold any value, similar to `any`, but with stricter type safety. - * Unlike `any`, you cannot perform operations directly on values of type `unknown` - * without type assertion or type narrowing. + * Checks if a given export type `T` is `unknown`. + * The `unknown` export type is the export type-safe counterpart of `any`. + * Values of export type `unknown` can hold any value, similar to `any`, but with stricter export type safety. + * Unlike `any`, you cannot perform operations directly on values of export type `unknown` + * without export type assertion or export type narrowing. * @returns `true` if `T` is `unknown`, otherwise `false`. * @example - * type UnknownValue = unknown; - * type IsUnknownValue = IsUnknown; => true + * export type UnknownValue = unknown; + * export type IsUnknownValue = IsUnknown; => true * - * type KnownValue = string; - * type IsNotUnknownValue = IsUnknown; => true + * export type KnownValue = string; + * export type IsNotUnknownValue = IsUnknown; => true */ export type IsUnknown = T extends unknown ? true : false; /** - * Represents the keys of a given type `T`. - * This type alias `Keys` is equivalent to `keyof T`, - * which retrieves the union type of keys (property names) of type `T`. - * @returns Union type of keys (property names) of type `T`. + * Represents the keys of a given export type `T`. + * This export type alias `Keys` is equivalent to `keyof T`, + * which retrieves the union export type of keys (property names) of export type `T`. + * @returns Union export type of keys (property names) of export type `T`. * @example - * type Person = { + * export type Person = { * name: string; * age: number; * email: string; * }; * - * type PersonKeys = Keys; => "name" | "age" | "email" + * export type PersonKeys = Keys; => "name" | "age" | "email" */ export type Keys = keyof T; /** - * Represents the union type of values of properties in a given type `T`. - * This type alias `Vals` retrieves the union type of values corresponding - * to the keys (property names) of type `T`. + * Represents the union export type of values of properties in a given export type `T`. + * This export type alias `Vals` retrieves the union export type of values corresponding + * to the keys (property names) of export type `T`. * @example - * type Person = { + * export type Person = { * name: string; * age: number; * email: string; * }; * - * type PersonValues = Vals; => string | number + * export type PersonValues = Vals; => string | number */ export type Vals = T[Keys]; /** - * Represents a type that can be either a single value of type `T` or an array of values of type `T`. + * Represents a export type that can be either a single value of export type `T` or an array of values of export type `T`. * @example - * type SingleOrArray = OneOrMany; + * export type SingleOrArray = OneOrMany; * * const value1: OneOrMany = 10; // Valid * const value2: OneOrMany = [20, 30]; // Also valid */ export type EitherOneOrMany = T | T[]; + +/** + * Represents a export type that can be used to construct a new instance. + * This export type is typically used to describe constructor functions or classes + * that can be invoked using the `new` keyword. + */ export type Newable = { new (...args: any[]): any }; + +/** + * Checks if a given export type `T` is a export type that can be used to construct a new instance (i.e., a `Newable` export type). + * The `Newable` export type typically represents constructor functions or classes that can be invoked using the `new` keyword. + * @returns `true` if `T` is a `Newable`, otherwise `false`. + */ export type IsNewable = T extends Newable ? true : false; + +/** + * Represents a export type that describes any function accepting any arguments + * and returning any value. + */ export type AnyFunction = (...args: any[]) => any; + +/** + * Represents a export type that describes any function accepting and retruning `unknown`s + */ export type UnknownFunction = (...args: unknown[]) => unknown; -export type Callable = (...args: A) => R; + +/** + * Conditional export type that checks if export type `T` extends export type `P`. + * If `T` extends `P`, the export type resolves to `Do`; otherwise, it resolves to `Else`. + * @example + * export type Result1 = IfExtends; // is true + * export type Result2 = IfExtends; // is true + * export type Result3 = IfExtends; // is false + * + * export type IsString = IfExtends; + * export type IsNumber = IfExtends; + * + * export type TestString = IsString; // is true + * export type TestNumber = IsNumber; // is true + * export type TestBoolean = IsNumber; // is false + */ export type IfExtends = T extends P ? Do : Else; + +/** + * Conditional export type that checks if two types `X` and `Y` are exactly equal. + * If `X` is equal to `Y`, the export type resolves to `true`; otherwise, it resolves to `false`. + * @example + * export type Result1 = Equals; // is true + * export type Result2 = Equals; // is false + * export type Result3 = Equals; // is true + */ export type Equals = (() => T extends X ? true : false) extends < T, >() => T extends Y ? true : false ? true : false; -// Typesafer way han maybe, think about hwen you have optional return types of -// functions, shoud u `return` ? with maybe ure free to do it or not -// but optional u have to return null, so the use case and handling are superior - +/** + * `Optional` is similar to Python's `Optional` and Rust's `Option` types. It solves + * the common pitfall associated with optional types in TypeScript, where using the `?` + * operator yields `T | undefined | null`. `Optional` promotes more predictable code + * by enforcing explicit handling of optional scenarios by requiring functions + * to return `null` specifically when a value is absent. + */ export type Optional = T | null; + +/** +This is the same as the `?` operator, try to use `Optional` instead +*/ export type Maybe = T | Nullable; export type MaybeUnknown = T | unknown; export type MaybeUndefined = T | undefined; +/** + A export type that excludes `null` and `undefined` from export type `T`. + * @example + Type Str = ExcludeNullable // string + Type Str2 = ExcludeNullable // string + Type Str3 = ExcludeNullable // string + + */ export type ExcludeNullable = Exclude; +/** + A export type that excludes `undefined` from export type `T`. + * @example + Type Str = ExcludeNullable // Result: string + */ export type ExcludeUndefined = Exclude; -export type ExcludeNull = Exclude; - -export type DeepExcludeNullable = { - [P in Keys]: DeepExcludeNullable>; -}; - -export type DeepPartial = { - [P in Keys]?: DeepPartial; -}; - -type X = { - readonly a: () => 1; - readonly b: string; - readonly c: { - readonly d: boolean; - readonly e: { - readonly g: { - readonly h: { - readonly i: true; - readonly j: 's'; - }; - readonly k: 'hello'; - }; - }; - }; -}; -type Expected = { - a: () => 1; - b: string; - c: { - d: boolean; - e: { - g: { - h: { - i: true; - j: 's'; - }; - k: 'hello'; - }; - }; - }; -}; +/** + A export type that excludes `null` from export type `T`. + * @example + Type Str = ExcludeNullable // Result: string + */ +export type ExcludeNull = Exclude; /** - * A type that recursively mutates all the proprties within a given object type `T`. + * A export type that recursively mutates all the proprties within a given object export type `T`. */ export type DeepMutable = T extends UnknownFunction ? T @@ -198,10 +227,9 @@ export type DeepMutable = T extends UnknownFunction * Checks if all the nested properties of a given object T is actually mutable. */ export type IsDeepMutable = T extends DeepMutable ? true : false; -export type ResultType = TestType, Expected, true>; /** - * A type that recursively turns the proprties within a given object type `T` immutable. + * A export type that recursively turns the proprties within a given object export type `T` immutable. */ export type DeepImmutable = T extends UnknownFunction ? T @@ -215,32 +243,18 @@ export type DeepImmutable = T extends UnknownFunction * Checks if all the nested properties of a given object T is actually immutable. */ export type IsDeepImmutable = T extends DeepImmutable ? true : false; -export type ResultType1 = TestType, true, true>; -export type ResultType2 = TestType, X, true>; -export type ResultType3 = DeepImmutable; - -export type AlterKeyTypeWith, R> = Pick< - T, - Exclude, K> -> & { [P in K]: R }; - -export type SwapKeysWithVals, Keys>> = { - [P in T[Keys]]: { - [K in Keys]: T[K] extends P ? K : never; - }[keyof T]; -}; declare const __s: unique symbol; /** - * This type alias defines a mechanism similar to Python's [`NewType`](https://docs.python.org/3/library/typing.html#newtype). + * This export type alias defines a mechanism similar to Python's [`NewType`](https://docs.python.org/3/library/typing.html#newtype). * In TypeScript world it's refered to as 'Branding'. - * The `NewType` encapsulates `N`, thus creating a distinct type that - * TypeScript's type system recognizes as separate from its underlying type. + * The `NewType` encapsulates `N`, thus creating a distinct export type that + * TypeScript's export type system recognizes as separate from its underlying export type. * @example - * type Foo = NewType; - * type Bar = NewType; - * type Baz = NewType; + * export type Foo = NewType; + * export type Bar = NewType; + * export type Baz = NewType; * * function fooBarBaz(foo: Foo, bar: Bar): Baz { * foo.concat(); // Allowed since Foo is an underlying string @@ -261,30 +275,71 @@ export type NewType = { [__s]: true; } & N; -export type Stretch = T extends object - ? T extends infer P - ? { [K in Keys

]: Stretch } - : never - : T; - -export type TypeGuard = (U: unknown) => U is T; - -export type FilterBy = { - [K in Keys]: K extends P ? K : never; -}[Keys]; +/** + * Type that recursively omits specified nested properties from an object type. + * @template T The input object type. + * @template P A string literal representing the path of properties to omit (e.g., 'person.name.value'). + * @example + * ```typescript + * type T = + * a: { + * b: string; + * b2: { + * c: { + * d: number; + * }; + * }; + * }; + * } + * + * DeepOmit // Results in: { a: { b: string; b2: {} } } + * ``` + */ +export type DeepOmit = P extends `${infer K}.${infer R}` + ? { + [KT in Keys]: KT extends K ? DeepOmit : T[KT]; + } + : Omit; -export type PickBy = Pick>; -export type OmitBy = Omit>; +export type EmptyArray = []; +/** + * Represents a export type that transposes a given 2D array `M`. + * It flips the matrix over its diagonal, switching its row and column indices. + * @template M - 2D array of any primitive export type values. + * + * @example + * // Transpose a 1x1 matrix + * export type Matrix = ArrayTranspose<[[1]]>; // expected to be [[1]] + * + * @example + * // Transpose a 2x2 matrix + * export type Matrix1 = ArrayTranspose<[[1, 'i'], [3, 4]]>; // expected to be [[1, 3], ["i", 4]] + * + * @example + * // Transpose a 2x3 matrix + * export type Matrix2 = ArrayTranspose<[[1, true, 3], [4, 5, 6]]>; // expected to be [[1, 4], [true, 5], [3, 6]] + * + */ +export type ArrayTranspose< + M extends Primitive[][], + N extends Primitive[] = M[0], +> = M extends EmptyArray + ? EmptyArray + : { + [KN in Keys]: { + [KM in Keys]: KN extends Keys ? M[KM][KN] : never; + }; + }; /** - * Represents a type that filters elements from an array based on a given predicate type. - * @typeParam T The array type to filter. - * @typeParam P The predicate type used for filtering elements from `T`. + * Represents a export type that filters elements from an array based on a given predicate export type. + * @typeParam T The array to filter. + * @typeParam P The predicate used for filtering elements from `T`. * @returns a new array type containing only the elements of `T` that match `P`. * @example * ```typescript - * type Numbers = [0, 1, 2, 3]; - * type FilteredNumbers = Filter; // Results in [0, 1] + * export type Numbers = [0, 1, 2, 3]; + * export type FilteredNumbers = Filter; // Results in [0, 1] * ``` */ export type ArrayFilter = T extends [ @@ -296,56 +351,60 @@ export type ArrayFilter = T extends [ : ArrayFilter : []; -export type EmptyObject = Exclude; - -export type OptionalKeys = { - [K in Keys]-?: EmptyObject extends Pick ? K : never; -}[keyof T]; - -export type DeepOptionalKeys = { - [K in Keys]-?: EmptyObject extends Pick ? OptionalKeys : never; -}[keyof T]; - -export type RequiredKeys = { - [K in Keys]-?: EmptyObject extends Pick ? never : K; -}[keyof T]; - -export type DeepRequiredKeys = { - [K in Keys]-?: EmptyObject extends Pick ? never : RequiredKeys; -}[keyof T]; - -export type MutableKeys = keyof { - [P in Keys as Equals, Readonly>> extends true +/** + * Retrieves the keys that are mutable from an object of export type T. + * @example + * ```typescript + * export type ExampleType = { + * a: number; + * readonly b: string; + * c: { + * a: string; + * d: { readonly x: Nullable; v: Maybe }; + * }; + * }; + * + * export type MutableKeysOfExampleType = MutableKeys; + * // Result: 'a' | 'c' + * ``` + */ +export type MutableKeys = { + [P in Keys]: Equals, Readonly>> extends true ? never - : P]: never; -}; - -export type Result3 = TestType< - MutableKeys<{ a: number; readonly b: string }>, - 'a', - true ->; + : P; +}[Keys]; -export type ImmutableKeys = keyof { - [P in Keys as Equals, Readonly>> extends true +/** + * Retrieves the keys that are immutable (readonly) from an object of export type T. + * @example + * ```typescript + * export type ExampleType = { + * a: number; + * readonly b: string; + * c: { + * a: string; + * d: { readonly x: Nullable; v: Maybe }; + * }; + * }; + * + * export type ImmutableKeysOfExampleType = ImmutableKeys; + * // Result: 'b' + * ``` + */ +export type ImmutableKeys = { + [P in Keys]: Equals, Readonly>> extends true ? P - : never]: never; -}; - -export type Result4 = TestType< - ImmutableKeys<{ a: number; readonly b: string }>, - 'b', - true ->; + : never; +}[Keys]; declare function _testType(): Equals< Equals, E >; /** - * Represents a type validation utility to determine if two types match. - * @template T1 The first type to compare. - * @template T2 The second type to compare. + * Determines if two types match. + * @template T1 The first export type to compare. + * @template T2 The second export type to compare. * @template Expected A boolean literal indicating whether `T1` should match `T2`. * If you expect the types to match, set this to true; if not, set it to false. * This utility will return a boolean that is true if your expectation was correct, otherwise false. @@ -354,38 +413,16 @@ export type TestType = ReturnType< typeof _testType >; -/* class is fucking lockedin ong! */ -export function locked(constructor: Newable): void { - const _sealAndFreeze = (obj: object): void => { - Object.seal(obj); - Object.freeze(obj); - Object.preventExtensions(obj); - }; - _sealAndFreeze(constructor); - _sealAndFreeze(constructor.prototype); -} - -export function final(target: T): T { - return class Final extends target { - constructor(...args: any[]) { - if (new.target !== Final) { - throw new Error(`${target.name} is final, you cannot extend it`); - } - super(...args); - } - }; -} - /** - * Represents a type that checks if a specified element type exists within an array. - * @typeParam T The array type to search within. - * @typeParam I The element type to check for existence in `T`. + * Represents a export type that checks if a specified element export type exists within an array. + * @typeParam T The array export type to search within. + * @typeParam I The element export type to check for existence in `T`. * @returns `true` if `I` is found within `T`, otherwise `false`. * @example * ```typescript - * type Numbers = [0, 1, 2]; - * type CheckZero = IsInArray; // true - * type CheckThree = IsInArray; // false + * export type Numbers = [0, 1, 2]; + * export type CheckZero = IsInArray; // true + * export type CheckThree = IsInArray; // false * ``` */ export type IsInArray = T extends [infer S, ...infer E] @@ -395,11 +432,11 @@ export type IsInArray = T extends [infer S, ...infer E] : false; /** - * A type that constructs a new array containing only unique elements from a given array type. - * @typeParam T The input array type from which unique elements are extracted. + * A export type that constructs a new array containing only unique elements from a given array export type. + * @typeParam T The input array export type from which unique elements are extracted. * @example * ```typescript - * type UArr = UniqueArray< + * export type UArr = UniqueArray< [unknown, unknown, 'foo', any, never, never, '33', 33, '33'] >; // => [unknown, 'foo', any, never, '33', 33] * ``` @@ -412,3 +449,69 @@ export type UniqueArray = T extends [ ? UniqueArray : UniqueArray : R; + +export type EmptyObject = NonNullable; + +export type AlterKeyTypeWith, R> = Pick< + T, + Exclude, K> +> & { [P in K]: R }; + +export type SwapKeysWithVals, Keys>> = { + [P in T[Keys]]: { + [K in Keys]: T[K] extends P ? K : never; + }[keyof T]; +}; + +export type Stretch = T extends object + ? T extends infer P + ? { [K in Keys

]: Stretch } + : never + : T; + +export type TypeGuard = (U: unknown) => U is T; + +export type FilterBy = { + [K in Keys]: K extends P ? K : never; +}[Keys]; + +export type PickBy = Pick>; +export type OmitBy = Omit>; + +export type OptionalKeys = { + [K in Keys]-?: EmptyObject extends Pick ? K : never; +}[keyof T]; + +export type DeepOptionalKeys = { + [K in Keys]-?: EmptyObject extends Pick ? OptionalKeys : never; +}[keyof T]; + +export type RequiredKeys = { + [K in Keys]-?: EmptyObject extends Pick ? never : K; +}[keyof T]; + +export type DeepRequiredKeys = { + [K in Keys]-?: EmptyObject extends Pick ? never : RequiredKeys; +}[keyof T]; + +/* class is fucking lockedin ong! */ +export function locked(constructor: Newable): void { + const _sealAndFreeze = (obj: object): void => { + Object.seal(obj); + Object.freeze(obj); + Object.preventExtensions(obj); + }; + _sealAndFreeze(constructor); + _sealAndFreeze(constructor.prototype); +} + +export function final(target: T): T { + return class Final extends target { + constructor(...args: any[]) { + if (new.target !== Final) { + throw new Error(`${target.name} is final, you cannot extend it`); + } + super(...args); + } + }; +} diff --git a/tests/array-transpose.test.ts b/tests/array-transpose.test.ts new file mode 100644 index 00000000..3a3ed653 --- /dev/null +++ b/tests/array-transpose.test.ts @@ -0,0 +1,34 @@ +import { Optional, Nullable, Numeric, TestType, ArrayTranspose } from 'src'; +import { test, expect } from 'vitest'; + +test('_', () => { + const result: TestType< + ArrayTranspose<[[1, Optional, Nullable, 3], [4, 5, 6]]>, + [[1, 4], [Optional, 5], [Nullable, 6], [3, never]], + true + > = true; + expect(result).toBe(true); +}); + +test('_', () => { + const result: TestType, [[1]], true> = true; + expect(result).toBe(true); +}); + +test('_', () => { + const result: TestType< + ArrayTranspose<[[1, true, 3], [4, 5, 6]]>, + [[1, 4], [true, 5], [3, 6]], + true + > = true; + expect(result).toBe(true); +}); + +test('_', () => { + const result: TestType< + ArrayTranspose<[[1, 'i'], [3, 4]]>, + [[1, 3], ['i', 4]], + true + > = true; + expect(result).toBe(true); +}); diff --git a/tests/deep-mutable.test.ts b/tests/deep-mutable.test.ts index 37e76d7f..40619684 100644 --- a/tests/deep-mutable.test.ts +++ b/tests/deep-mutable.test.ts @@ -12,13 +12,13 @@ import { test, expect } from 'vitest'; type Actual = { readonly a: () => 1; readonly x: string; - readonly s: { + readonly s?: { readonly q: Nullable; readonly s: { readonly i: { - readonly x: { + readonly x?: { readonly o: Maybe; - readonly n: Falsy; + readonly n?: Falsy; }; readonly e: 'foo'; }; @@ -28,13 +28,13 @@ type Actual = { type Expected = { a: () => 1; x: string; - s: { + s?: { q: Nullable; s: { i: { - x: { + x?: { o: Maybe; - n: Falsy; + n?: Falsy; }; e: 'foo'; }; diff --git a/tests/deep-omit.test.ts b/tests/deep-omit.test.ts new file mode 100644 index 00000000..6eafedf8 --- /dev/null +++ b/tests/deep-omit.test.ts @@ -0,0 +1,41 @@ +import { DeepOmit, TestType, EmptyObject } from 'src'; +import { test, expect } from 'vitest'; + +type Actual = { + a: { + b: string; + b2: { + c: { + d: number; + }; + }; + }; +}; + +test('_', () => { + const result: TestType< + DeepOmit, + { a: { b: string; b2: EmptyObject } }, + true + > = true; + expect(result).toBe(true); +}); + +test('_', () => { + const result: TestType< + DeepOmit, + { a: { b: string; b2: EmptyObject } }, + true + > = true; + expect(result).toBe(true); +}); + +test('_', () => { + const result: TestType, EmptyObject, true> = true; + expect(result).toBe(true); +}); + +test('_', () => { + const result: TestType, Actual, true> = true; + expect(result).toBe(true); +}); diff --git a/tests/if-extends.test.ts b/tests/if-extends.test.ts new file mode 100644 index 00000000..732b9009 --- /dev/null +++ b/tests/if-extends.test.ts @@ -0,0 +1,41 @@ +import { IfExtends, TestType } from 'src'; +import { test, expect } from 'vitest'; + +test('_', () => { + const result: TestType< + IfExtends, + true, + true + > = true; + expect(result).toBe(true); +}); + +test('_', () => { + const result: TestType< + IfExtends, + true, + true + > = true; + expect(result).toBe(true); +}); + +test('_', () => { + const result: TestType< + IfExtends, + false, + true + > = true; + expect(result).toBe(true); +}); + +test('_', () => { + type _IsString = IfExtends; + const result: TestType<_IsString, true, true> = true; + expect(result).toBe(true); +}); + +test('_', () => { + type _IsNumber = IfExtends; + const result: TestType<_IsNumber, true, true> = true; + expect(result).toBe(true); +}); diff --git a/tests/immutable-keys.ts b/tests/immutable-keys.ts new file mode 100644 index 00000000..6984ec0c --- /dev/null +++ b/tests/immutable-keys.ts @@ -0,0 +1,18 @@ +import { ImmutableKeys, Maybe, Nullable, TestType, Newable } from 'src'; +import { test, expect } from 'vitest'; + +test('does not go deep', () => { + const result: TestType< + ImmutableKeys<{ + a: number; + readonly b: string; + c: { + a: string; + d: { readonly x: Nullable; v: Maybe }; + }; + }>, + 'b', + true + > = true; + expect(result).toBe(true); +}); diff --git a/tests/mutable-keys.test.ts b/tests/mutable-keys.test.ts new file mode 100644 index 00000000..7221c795 --- /dev/null +++ b/tests/mutable-keys.test.ts @@ -0,0 +1,18 @@ +import { MutableKeys, Maybe, Nullable, TestType, Newable } from 'src'; +import { test, expect } from 'vitest'; + +test('does not go deep', () => { + const result: TestType< + MutableKeys<{ + a: number; + readonly b: string; + c: { + a: string; + d: { readonly x: Nullable; v: Maybe }; + }; + }>, + 'a' | 'c', + true + > = true; + expect(result).toBe(true); +}); diff --git a/tests/test-all-excludes.test.ts b/tests/test-all-excludes.test.ts new file mode 100644 index 00000000..91735d3a --- /dev/null +++ b/tests/test-all-excludes.test.ts @@ -0,0 +1,49 @@ +import { ExcludeNullable, Nullable, Primitive, TestType } from 'src'; +import { test, expect } from 'vitest'; + +test('_', () => { + const result: TestType, string, true> = true; + expect(result).toBe(true); +}); + +test('_', () => { + const result: TestType, string, true> = true; + expect(result).toBe(true); +}); + +test('_', () => { + const result: TestType, never, true> = true; + expect(result).toBe(true); +}); + +test('only works on unions', () => { + const result: TestType< + ExcludeNullable, + string | number | bigint | boolean | symbol, + true + > = true; + expect(result).toBe(true); +}); + +test('does not work on objects', () => { + const result: TestType< + ExcludeNullable<{ + a: string; + b: Nullable; + c: unknown; + d: { + e: null; + }; + }>, + { + a: string; + b: Nullable; + c: unknown; + d: { + e: null; + }; + }, + true + > = true; + expect(result).toBe(true); +});