<div align="center">

# ts-roids

100+  types and decorators to bullet proof TypeScript even more.

[![CI](https://github.com/AshGw/ts-roids/actions/workflows/ci.yml/badge.svg)](https://github.com/AshGw/ts-roids/actions/workflows/ci.yml)
[![@latest](https://img.shields.io/npm/v/ts-roids.svg)](https://www.npmjs.com/package/ts-roids)
[![npm downloads](https://img.shields.io/npm/dm/ts-utils.svg)](https://www.npmjs.com/package/ts-roids)
<hr/>
</div>

## Installation
**npm**
```bash
npm i ts-roids
```
**pnpm**
```bash
pnpm i ts-roids
```
If you're only using types, you can install it as a ``devDependency``.
And if you're using decorators, set this.
```json
{
  "compilerOptions": {
    // ...
    "experimentalDecorators": true
  }
}
```
Requires TypesScript `v5.0`+
## Documentation
Checkout the full [API reference](https://ts-roids.ashgw.me/) for all usage examples with details.

#### Types

- [`ExclusiveUnion<T>`](https://ts-roids.ashgw.me/types/ExclusiveUnion
- .html) - Creates a union type where each variant has its specific properties required, while other properties are optional.
- [`UniqueArray<T>`](https://ts-roids.ashgw.me/types/UniqueArray.html) - Create unique array type from a given array type `T`, a set perse.
- [`Prune<T,N = NotIncluded>`](https://ts-roids.ashgw.me/types/Prune.html) - Prune a type `T` by recursively omitting properties of type `N` (defaults to [`NotIncluded`](https://ts-roids.ashgw.me/types/NotIncluded.html)).
- [`PositiveRange<N,M>`](https://ts-roids.ashgw.me/types/PositiveRange.html) - Represents a range of positive integers from N to M (inclusive), useful for enforcing numeric bounds.
- [`UnionToTuple<T>`](https://ts-roids.ashgw.me/types/UnionToTuple.html) - Converts a union type `T` into a tuple type, allowing for ordered access to the union's members.
- [`EnforcedString`](https://ts-roids.ashgw.me/types/EnforcedString.html) - Check if a string starts with a given prefix and ends with a given suffix.
- [`DeepToPrimitive<Obj>`](https://ts-roids.ashgw.me/types/DeepToPrimitive.html) - Recursively transforms an object type T into a type where all properties are replaced with their corresponding primitive types.
- [`Assign<Obj,ObjArr>`](https://ts-roids.ashgw.me/types/Assign.html) - Copies all enumerable own properties from one target object to a source array of objects.
- [`CapitalizeFirst<T>`](https://ts-roids.ashgw.me/types/CapitalizeFirst.html) - Capitalizes the first character of a string literal type while preserving the rest.
- [`Flip<Obj>`](https://ts-roids.ashgw.me/types/Flip.html) - Flips keys with values of an object type `Obj`.
- [`DeepImmutable<Obj>`](https://ts-roids.ashgw.me/types/DeepImmutable.html) - Recursively turns the proprties within a given object type ``T`` immutable, as in have all the properties with the `readonly` modifier.
- [`Deepmutable<Obj>`](https://ts-roids.ashgw.me/types/DeepMutable.html) - Recursively mutates all the proprties within a given object type ``T``, as in have all the properties without the `readonly` modifier.
- [`DeepRequired<Obj>`](https://ts-roids.ashgw.me/types/DeepRequired.html) - Recursively make all object properties required.
- [`DeepNotRequired<Obj>`](https://ts-roids.ashgw.me/types/DeepNotRequired.html) - Recursively make all object properties not required.
- [`DeepOmit<Obj,P>`](https://ts-roids.ashgw.me/types/DeepOmit.html) - Recursively omits specified nested properties from an object, based on a given predicate `P`.
- [`DeepPick<Obj,P>`](https://ts-roids.ashgw.me/types/DeepPick.html) - Deeply pick properties from a nested object, based on a given predicate `P`.
- [`EmptyObject`](https://ts-roids.ashgw.me/types/EmptyObject.html) - Represents any non-nullish value, basically `{}`.
- [`EqualStrlen<S1, S2>`](https://ts-roids.ashgw.me/types/EqualStrlen.html) - Check if two strings ``S1`` and ``S2`` have the same length.
- [`PartialExcept<T, P>`](https://ts-roids.ashgw.me/types/PartialExcept.html) - Makes all properties in `T` optional except those in `K` which remain required.
- [`FilterBy<Obj, P>`](https://ts-roids.ashgw.me/types/FilterBy.html) -  Filters keys from the object type `Obj` based on a specified predicate ``P``.
- [`Float<N>`](https://ts-roids.ashgw.me/types/Float.html) - Type representing a float.
- [`If<C, Do, Else>`](https://ts-roids.ashgw.me/types/If.html) - If ``C`` evaluates ``true``, ``Do``, otherwise return ``Else``.
- [`IfEquals<T, P, Do, Else>`](https://ts-roids.ashgw.me/types/IfEquals.html) - Checks if type ``T`` is equal to type ``P``. If ``T`` is equal to ``P``, the type resolves to ``Do``, otherwise ``Else``.
- [`IfExtends<T, P, Do, Else>`](https://ts-roids.ashgw.me/types/IfExtends.html) -  Checks if type ``T`` extends type ``P``. if it does, the type resolves to ``Do``, otherwise ``Else``.
- [`ImmutableKeys<Obj>`](https://ts-roids.ashgw.me/types/ImmutableKeys.html) - Retrieves the keys that are immutable (``readonly``) from an object of type ``Obj``.
- [`Integer<N>`](https://ts-roids.ashgw.me/types/Integer.html) - Represents an integer.
- [`Abs<N>`](https://ts-roids.ashgw.me/types/Abs.html) - Get the absolute value of a [``Numeric``](https://ts-roids.ashgw.me/types/Numeric.html).
- [`And<B1,B2>`](https://ts-roids.ashgw.me/types/And.html) - Logical AND between two boolean types.
- [`EitherOneOrMany<T>`](https://ts-roids.ashgw.me/types/EitherOneOrMany.html) - Represents a type that can be either a single value of type ``T`` or an array of values of type ``T``.
- [`Nullable`](https://ts-roids.ashgw.me/types/Nullable.html) - Represents any non-nullish value, basically `{}`.
- [`Equals<X,Y>`](https://ts-roids.ashgw.me/types/Equals.html) - Checks if two types ``X`` and ``Y`` are exactly equal.
- [`EvenNumeric<T>`](https://ts-roids.ashgw.me/types/EvenNumeric.html) - Represents an even [`Numeric`](https://ts-roids.ashgw.me/types/Numeric.html).
- [`ExcludeNull<T>`](https://ts-roids.ashgw.me/types/ExcludeNull.html) - Excludes ``null`` from a type ``T``.
- [`ExcludeNullable<T>`](https://ts-roids.ashgw.me/types/ExcludeNullable.html) - Excludes [`Nullable`](https://ts-roids.ashgw.me/types/Nullable.html) from a type ``T``.
- [`ExcludeUndefined<T>`](https://ts-roids.ashgw.me/types/ExcludeUndefined.html) - Excludes `undefined` from a type ``T``.
- [`KeysOfUnion<T>`](https://ts-roids.ashgw.me/types/KeysOfUnion.html) - Extracts the union of keys from a given union of object types, useful for accessing all possible keys in unions.
- [`Simplify<T>`](https://ts-roids.ashgw.me/types/Simplify.html) - Flattens the structure of a type by resolving intersections and simplifying nested mapped types, enhancing readability.
- [`Extends<T,U>`](https://ts-roids.ashgw.me/types/Extends.html) - Evaluates whether one type ``T`` is assignable to another type ``U``.
- [`Falsy`](https://ts-roids.ashgw.me/types/Falsy.html) - Represents a type that is [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy)  JavaScript.
- [`FalsyProperties<T>`](https://ts-roids.ashgw.me/types/FalsyProperties.html) - Extracts falsy properties from an object type ``T``.
- [`IsArrayIncludesTypeof<Arr, T>`](https://ts-roids.ashgw.me/types/IsArrayIncludesTypeof.html) - Checks if an array type `Arr` includes one or more of `T` type.
- [`IsBigInt<T>`](https://ts-roids.ashgw.me/types/IsBigInt.html) - Checks if `T` is a ``bigint``.
- [`IsBoolean<T>`](https://ts-roids.ashgw.me/types/IsBoolean.html) -  Checks if `T` is a ``boolean``.
- [`IsDeepImmutable<Obj>`](https://ts-roids.ashgw.me/types/IsDeepImmutable.html) - Checks if all the nested properties of a given object ``Obj`` are immutable.
- [`IsDeepMutable<Obj>`](https://ts-roids.ashgw.me/types/IsDeepMutable.html) - Checks if all the nested properties of a given object ``Obj`` are  mutable.
- [`IsDeepNotRequired<Obj>`](https://ts-roids.ashgw.me/types/IsDeepNotRequired.html) - Checks if all the properties of a given object (nested) are not required, as in, all properties have the `?` modifier.
- [`IsDeepRequired<Obj>`](https://ts-roids.ashgw.me/types/IsDeepRequired.html) - Checks if all the properties of a given object (nested) are required, as in, all properties do not have the `?` modifier.
- [`IsExactlyAny<T>`](https://ts-roids.ashgw.me/types/IsExactlyAny.html) - Checks if a type `T` is exactly `any`.
- [`IsExactlyBigInt<T>`](https://ts-roids.ashgw.me/types/IsExactlyBigInt.html) - Checks if a type `T` is exactly `bigint` not a subtype of it.
- [`IsExactlyNumber<T>`](https://ts-roids.ashgw.me/types/IsExactlyNumber.html) - Checks if a type `T` is exactly ``number`` not a subtype of it.
- [`IsExactlyString<T>`](https://ts-roids.ashgw.me/types/IsExactlyString.html) - Checks if a type `T` is exactly ``string`` not a subtype of it.
- [`IsExactlySymbol<T>`](https://ts-roids.ashgw.me/types/IsExactlySymbol.html) - Checks if a type `T` is exactly ``symbol`` not a subtype of it.
- [`IsExactlyUnknown<T>`](https://ts-roids.ashgw.me/types/IsExactlyUnknown.html) - Checks if a type `T` is exactly ``unknown`` not a subtype of it.
- [`IsFalsy<T>`](https://ts-roids.ashgw.me/types/IsFalsy.html) - Checks if a given type ``T`` is [``Falsy``](https://ts-roids.ashgw.me/types/Falsy.html).
- [`IsFloat<N>`](https://ts-roids.ashgw.me/types/IsFloat.html) - Checks if a given type ``T`` is a [``Float<N>``](https://ts-roids.ashgw.me/types/Float.html).
- [`IsFunction<T>`](https://ts-roids.ashgw.me/types/IsFunction.html) - Checks if a given type ``T`` is a function.
- [`IsInteger<N>`](https://ts-roids.ashgw.me/types/IsInteger.html) - Checks if a given [``Numeric``](https://ts-roids.ashgw.me/types/Numeric.html)  is an [``Integer<N>``](https://ts-roids.ashgw.me/types/Integer.html).
- [`IsNever<T>:`](https://ts-roids.ashgw.me/types/IsNever.html) - Checks if a type `T` does not resolve, so `never`.
- [`IsNewable<T>`](https://ts-roids.ashgw.me/types/IsNewable.html) -  Checks if a type `T` is [``Newable``](https://ts-roids.ashgw.me/types/Newable.html).
- [`IsNullable<T>`](https://ts-roids.ashgw.me/types/IsNullable.html) -  Checks if a type `T` is [``Nullable``](https://ts-roids.ashgw.me/types/Nullable.html).
- [`IsNumber<T>`](https://ts-roids.ashgw.me/types/IsNumber.html) - Checks if a type `T` is a `number`.
- [`IsNumeric<T>`](https://ts-roids.ashgw.me/types/IsNumeric.html) - Checks if a type `T` is [``Numeric``](https://ts-roids.ashgw.me/types/Numeric.html).
- [`IsObject<T>`](https://ts-roids.ashgw.me/types/IsObject.html) -  Checks if a given type `T` qualifies as an object.
- [`IsString<T>`](https://ts-roids.ashgw.me/types/IsString.html) - Check if a given type `T` is a ``string``.
- [`IsSymbol<T>`](https://ts-roids.ashgw.me/types/IsSymbol.html) - Check if a given type `T` is a ``symbol``.
- [`IsTruthy<T>`](https://ts-roids.ashgw.me/types/IsTruthy.html) - Check if a given type `T` resolves to a truthy value.
- [`Keys<T>`](https://ts-roids.ashgw.me/types/Keys.html) - Retrieves the union type of keys (property names) of a type ``T``.
- [`Maybe<T>`](https://ts-roids.ashgw.me/types/Maybe.html) - Type that might be [``Nullable``](https://ts-roids.ashgw.me/types/Nullable.html)
- [`MaybeUndefined<T>`](https://ts-roids.ashgw.me/types/MaybeUndefined.html) - Type that might ``undefined``.
- [`MutableKeys<Obj>`](https://ts-roids.ashgw.me/types/MutableKeys.html) - Retrieves the keys that are mutable from an object of type ``Obj``.
- [`Nand<B1, B2>`](https://ts-roids.ashgw.me/types/Nand.html) - Logical ``NAND`` between two boolean types ``B1`` and ``B2``.
- [`NegativeFloat<N>`](https://ts-roids.ashgw.me/types/NegativeFloat.html) - Represents a negative (\]-∞, 0\[) [``Float<N>``](https://ts-roids.ashgw.me/types/Float.html).
- [`NegativeFloatString<S>`](https://ts-roids.ashgw.me/types/NegativeFloatString.html) - Represents a negative [``Float<N>``](https://ts-roids.ashgw.me/types/Float.html) parsed from a ``string``.
- [`NegativeInteger<N>`](https://ts-roids.ashgw.me/types/NegativeInteger.html) -  Represents a negative (\]-∞, 0\[) [``Integer<N>``](https://ts-roids.ashgw.me/types/Integer.html).
- [`NegativeIntegerString<S>`](https://ts-roids.ashgw.me/types/NegativeIntegerString.html) - Represents a negative [``Integer<N>``](https://ts-roids.ashgw.me/types/Integer.html) parsed from a ``string``.
- [`NewType<New, Base>`](https://ts-roids.ashgw.me/types/NewType.html) -  Represents a new unique type derived from an existing base type. (branded type)
- [`Newable`](https://ts-roids.ashgw.me/types/Newable.html) - Represents constructor functions that can be invoked using the new keyword.
- [`NonRequiredKeys<Obj>`](https://ts-roids.ashgw.me/types/NonRequiredKeys.html) - Returns all non required keys of an object `Obj`, as in any property of an object that is marked with `?` operator.
- [`Not<B>`](https://ts-roids.ashgw.me/types/Not.html) - Negates a boolean type `B`.
- [`Nullable`](https://ts-roids.ashgw.me/types/Nullable.html) - Represents a type that can either be  ``null`` or ``undefined``.
- [`Numeric`](https://ts-roids.ashgw.me/types/Numeric.html) - Represents a type that can either be  ``number`` or ``bigint``.
- [`NumerifyString<S>`](https://ts-roids.ashgw.me/types/NumerifyString.html) - Turn a given string literal to a [``Numeric``](https://ts-roids.ashgw.me/types/Numeric.html), if possible.
- [`Methods<Obj>`](https://ts-roids.ashgw.me/types/Methods.html) - Get the literal names of keys that are methods in an object type `Obj`.
- [`Properties<Obj>`](https://ts-roids.ashgw.me/types/Properties.html) - Get the literal names of keys that are properties in an object type `Obj`.
- [`OddNumeric<T>`](https://ts-roids.ashgw.me/types/OmitByType.html) - Represents an odd [`Numeric`](https://ts-roids.ashgw.me/types/OddNumeric.html).
- [`OmitByType<Obj, T>`](https://ts-roids.ashgw.me/types/OmitByType.html) - Get a set of properties from `Obj` whose type are not assignable to ``T``.
- [`OmitCommonKeys<Obj1, Obj2>`](https://ts-roids.ashgw.me/types/OmitCommonKeys.html) - Omit any common key between the the two objects,.
- [`OmitExactlyByType<Obj, T>`](https://ts-roids.ashgw.me/types/OmitExactlyByType.html) - Omit properties from ``Obj`` whose type exactly matches ``T``.
- [`Optional<T>`](https://ts-roids.ashgw.me/types/Optional.html) - Represents a type that may be `null`, similar to Python's ``Optional`` type and Rust's ``Option`` enum.
- [`Or<B1, B2>`](https://ts-roids.ashgw.me/types/Or.html) - Logical ``OR`` between two boolean types ``B1`` and ``B2``.
- [`PickByType<Obj, T>`](https://ts-roids.ashgw.me/types/PickByType.html) - Pick from `Obj` a set of properties that match the type `T`.
- [`PickCommonKeys<Obj1, Obj2>`](https://ts-roids.ashgw.me/types/PickCommonKeys.html) - Get the common keys between two objects.
- [`PickExactlyByType<Obj, T>`](https://ts-roids.ashgw.me/types/PickExactlyByType.html) - Get a set of properties from ``Obj`` whose type exactly matches ``T``.
- [`PositiveFloat<N>`](https://ts-roids.ashgw.me/types/PositiveFloat.html) - Represents a positive (\[0, +∞\[) [``Float<N>``](https://ts-roids.ashgw.me/types/Float.html).
- [`PositiveFloatString<S>`](https://ts-roids.ashgw.me/types/PositiveFloatString.html) - Represents a positive [``Float<N>``](https://ts-roids.ashgw.me/types/Float.html) parsed from a ``string``.
- [`PositiveInteger<N>`](https://ts-roids.ashgw.me/types/PositiveInteger.html) - Represents a positive (\[0, +∞\[) [``Integer<N>``](https://ts-roids.ashgw.me/types/Integer.html).
- [`PositiveIntegerString<S>`](https://ts-roids.ashgw.me/types/PositiveIntegerString.html) - Represents a negative [``Integer<N>``](https://ts-roids.ashgw.me/types/Integer.html) parsed from a ``string``.
- [`Primitive`](https://ts-roids.ashgw.me/types/Primitive.html) - All [primitive](https://developer.mozilla.org/en-US/docs/Glossary/Primitive) types.
- [`ReplaceKeys<Obj1,P,Obj2>`](https://ts-roids.ashgw.me/types/ReplaceKeys.html) - Constructs a new type by replacing properties `P` of type ``Obj`` with object type ``Obj2``.
- [`RequiredKeys<Obj>`](https://ts-roids.ashgw.me/types/RequiredKeys.html) - Get the required keys of an object (shallow).
- [`SizedTuple<T,N>`](https://ts-roids.ashgw.me/types/SizedTuple.html) -  Creates a tuple with a specific length, where each element is of a given type.
- [`StringEndsWith<S,E>`](https://ts-roids.ashgw.me/types/StringEndsWith.html) - Checks if a string `S` ends with `E`
- [`StringStartsWith<S,St>`](https://ts-roids.ashgw.me/types/StringStartsWith.html) -  Checks if a string `S` starts with `St`
- [`StringifyPrimitive<P>`](https://ts-roids.ashgw.me/types/StringifyPrimitive.html) - Turns a given [``Primitive``](https://ts-roids.ashgw.me/types/Primitive.html) value (except ``symbol``) into its string representation.
- [`Strlen<S>`](https://ts-roids.ashgw.me/types/Strlen.html) - Get the length of a string `S`.
- [`TestType<T1, T2, Expected>`](https://ts-roids.ashgw.me/types/TestType.html) - Tests if type `T1` and `T2` are the same.
- [`TruthyProperties<T>`](https://ts-roids.ashgw.me/types/TruthyProperties.html) - Extracts truthy properties from an object type ``T``.
- [`UnionToIntersection<U>`](https://ts-roids.ashgw.me/types/UnionToIntersection.html) - As the name implies, it turns a union into an intersection.
- [`Vals<Obj>`](https://ts-roids.ashgw.me/types/Vals.html) - Get the set of type values in a given object.
- [`Xor<B1, B2>`](https://ts-roids.ashgw.me/types/Xor.html) - Exclusive ``OR`` between two boolean types ``B1`` and ``B2``.


The best way to understand how these types work is to check the [tests directory](/tests/). Each type has corresponding ~~edge~~ test cases that demonstrate its usage and expected behavior.


#### Decorators

- [`@Final`]() - Marks an object final, as in one cannot inherit from it.
- [`@Sealed`]() - [Seals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal) an object.
- [`@Frozen`]() - [Freezes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) an object.
- [`@Singleton`]() -  Ensures that only a single instance of the class can be created.

#### Basic Usage
Finalize and freeze objects

```ts
import type { Optional, NewType, MaybeUndefined } from 'ts-roids';
import { Final, Frozen, Singleton } from 'ts-roids';

type Bar = NewType<'Bar', string>;
type Baz = NewType<'Baz', string>;
type Secret = NewType<'Secret', string>;

abstract class BaseFoo<T> {
  public abstract requestFoo(secret: Secret, baz: Baz): Promise<Optional<T>>;
}

@Final
@Frozen
@Singleton
class Foo<T> extends BaseFoo<T> {
  private static readonly rnd = Math.random();
  private readonly foo: T;
  public bar: Optional<Bar>; // `Bar` then becomes readonly with the decorator

  public constructor(foo: T, bar?: MaybeUndefined<Bar>) {
    super();
    this.foo = foo;
    this.bar = bar ?? null;
  }

  public override async requestFoo(
    secret: Secret,
    baz: Baz
  ): Promise<Optional<T>> {
    if (
      Foo.rnd > 0.5 &&
      secret.concat().toLowerCase() === '123' &&
      baz.concat().toLowerCase() === 'baz' &&
      this.bar !== null
    ) {
      return await Promise.resolve(this.foo);
    }

    return null;
  }
}

class SubFoo extends Foo<string> {
  constructor(foo: string) {
    super(foo);
  }
}

// No problem with instantiation
const foo = new Foo('foo');

// The Singleton ensures the same instance is returned
const foo2 = new Foo('bar');
console.log(foo2 === foo); // True

// Since the object is final:
// The line below will cause a TypeError: Cannot inherit from the final class Foo
new SubFoo('subFoo');

// Since the object is frozen:
// The line below will cause a TypeError: Cannot add property 'requestFoo', object is not extensible
foo.requestFoo = async () => {
  return await Promise.resolve('not foo');
};

// The line below will cause a TypeError: Cannot assign to read only property 'bar'
foo.bar = 'not bar' as Bar;
```
 The TypeScript team has not yet introduced a built-in final modifier yet, check
[this](https://github.com/microsoft/TypeScript/issues/8306), [this](https://github.com/microsoft/TypeScript/issues/50532) and many other requests.
Although they introduced `override` in [`v4.3`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-3.html#override-and-the---noimplicitoverride-flag) .

Decorators like ``@Final`` provide a limited way to emulate final behavior, these are merely band-aids for now, until TS officially supports a true final modifier.

You can also seal an object btw.
```ts
@Sealed
class Person {
  constructor(name: string, age?: number) {}
}

const john = new Person('John', 30);

// Existing properties can still be modified
john.age = 31; // No Errors

// Existing properties cannot be re-configured nor deleted

(john as any).email = 'john@doe.com'; // TypeError: Cannot add property email,
// object is not extensible

delete john.age; // TypeError: Cannot delete property 'age'
```

## Changelog

See [releases](https://github.com/ashgw/ts-roids/releases).

## License
[GPL-3](/LICENSE)
