ts-essentials
    TypeScript icon, indicating that this package has built-in type declarations

    7.0.2 • Public • Published

    ts-essentials

    ts-essentials

    All essential TypeScript types in one place 🤙

    Downloads Build status Software License All Contributors codechecks.io

    Install

    npm install --save-dev ts-essentials

    👉 We require typescript>=3.7. If you're looking for support for older TS versions use ts-essentials@3 (for 3.6>=) or ts-essentials@2 instead. If you use any functions you should add ts-essentials to your dependencies (npm install --save ts-essentials) to avoid runtime errors in production.

    What's inside?

    Basic

    • Primitive type matching all primitive values.
    • noop function that takes any arguments and returns nothing, as a placeholder for e.g. callbacks.

    Dictionaries

    keywords: map

    const stringDict: Dictionary<string> = {
      a: "A",
      b: "B",
    };
    
    // Specify second type argument to change dictionary keys type
    const dictOfNumbers: Dictionary<string, number> = {
      420: "four twenty",
      1337: "HAX",
    };
    
    // You may specify union types as key to cover all possible cases. It acts the same as Record from TS's standard library
    export type DummyOptions = "open" | "closed" | "unknown";
    const dictFromUnionType: Dictionary<number, DummyOptions> = {
      closed: 1,
      open: 2,
      unknown: 3,
    };
    
    // and get dictionary values
    type stringDictValues = DictionaryValues<typeof stringDict>;
    // Result: string
    
    // When building a map using JS objects consider using SafeDictionary
    const safeDict: SafeDictionary<number> = {};
    const value: number | undefined = safeDict["foo"];
    
    // With SafeDictionary you don't need to use all of the sub-types of a finite type.
    // If you care about the key exhaustiveness, use a regular Dictionary.
    type ConfigKeys = "LOGLEVEL" | "PORT" | "DEBUG";
    const configSafeDict: SafeDictionary<number, ConfigKeys> = {
      LOGLEVEL: 2,
    };
    const maybePort: number | undefined = configSafeDict["PORT"];
    
    const configDict: Dictionary<number, ConfigKeys> = {
      LOGLEVEL: 2,
      PORT: 8080,
      DEBUG: 1,
    };
    const port: number = configDict["PORT"];

    Deep* wrapper types

    • DeepPartial
    • DeepRequired
    • DeepReadonly
    • DeepNonNullable
    • DeepNullable
    • DeepUndefinable

    keywords: recursive, nested, optional

    type ComplexObject = {
      simple: number;
      nested: {
        a: string;
        array: [{ bar: number }];
      };
    };
    
    type ComplexObjectPartial = DeepPartial<ComplexObject>;
    const samplePartial: ComplexObjectPartial = {
      nested: {
        array: [{}],
      },
    };
    
    type ComplexObjectAgain = DeepRequired<ComplexObjectPartial>;
    const sampleRequired: ComplexObjectAgain = {
      simple: 5,
      nested: {
        a: "test",
        array: [{ bar: 1 }],
      },
    };
    
    type ComplexObjectReadonly = DeepReadonly<ComplexObject>;
    
    type ComplexNullableObject = {
      simple: number | null | undefined;
      nested: {
        a: string | null | undefined;
        array: [{ bar: number | null | undefined }] | null | undefined;
      };
    };
    
    type ComplexObjectNonNullable = DeepNonNullable<ComplexNullableObject>;
    const sampleNonNullable: ComplexObjectNonNullable = {
      simple: 5,
      nested: {
        a: "test",
        array: [{ bar: null }], // Error: Type 'null' is not assignable to type 'number'
      },
    };
    
    type ComplexObjectNullable = DeepNullable<ComplexObject>;
    const sampleDeepNullable1: ComplexObjectNullable = {
      simple: null,
      nested: {
        a: null,
        array: [{ bar: null }],
      },
    };
    const sampleDeepNullable2: ComplexObjectNullable = {
      simple: 1,
      nested: {
        array: [null], // OK
        // error -- property `a` missing, should be `number | null`
      },
    };
    
    // DeepUndefinable will come in handy if:
    //  - you want to explicitly assign values to all of the properties
    //  AND
    //  - the expression used for the assignment can return an `undefined` value
    // In most situations DeepPartial will suffice.
    declare function tryGet(name: string): string | undefined;
    type ComplexObjectUndefinable = DeepUndefinable<ComplexObject>;
    const sampleDeepUndefinable1: ComplexObjectUndefinable = {
      simple: undefined,
      nested: {
        a: tryGet("a-value"),
        array: [{ bar: tryGet("bar-value") }],
      },
    };
    const sampleDeepUndefinable2: ComplexObjectUndefinable = {
      // error -- property `simple` missing, should be `number | undefined`
      nested: {
        array: [[{ bar: undefined }]],
        // error -- property `a` missing, should be `string | undefined`
      },
    };

    Writable

    Make all attributes of object writable.

    type Foo = {
      readonly a: number;
      readonly b: string;
    };
    
    const foo: Foo = { a: 1, b: "b" };
    (foo as Writable<typeof foo>).a = 42;
    type Foo = {
      readonly foo: string;
      bar: {
        readonly x: number;
      };
    }[];
    
    const test: DeepWritable<Foo> = [
      {
        foo: "a",
        bar: {
          x: 5,
        },
      },
    ];
    
    // we can freely write to this object
    test[0].foo = "b";
    test[0].bar.x = 2;

    Buildable

    keywords: builder

    A combination of both DeepWritable and DeepPartial. This type allows building an object step-by-step by assigning values to its attributes in multiple statements.

    interface ReadonlyObject
      extends Readonly<{
        simple: number;
        nested: Readonly<{
          a: string;
          array: ReadonlyArray<Readonly<{ bar: number }>>;
        }>;
      }> {}
    
    const buildable: Buildable<ReadonlyObject> = {};
    buildable.simple = 7;
    buildable.nested = {};
    buildable.nested.a = "test";
    buildable.nested.array = [];
    buildable.nested.array.push({ bar: 1 });
    const finished = buildable as ReadonlyObject;

    Omit

    Our version of Omit is renamed to StrictOmit in v3, since the builtin Omit has become part of TypeScript 3.5

    StrictOmit

    Usage is similar to the builtin version, but checks the filter type more strictly.

    type ComplexObject = {
      simple: number;
      nested: {
        a: string;
        array: [{ bar: number }];
      };
    };
    
    type SimplifiedComplexObject = StrictOmit<ComplexObject, "nested">;
    
    // Result:
    // {
    //  simple: number
    // }
    
    // if you want to Omit multiple properties just use union type:
    type SimplifiedComplexObject = StrictOmit<ComplexObject, "nested" | "simple">;
    
    // Result:
    // { } (empty type)

    Comparison between Omit and StrictOmit

    Following the code above, we can compare the behavior of Omit and StrictOmit.

    type SimplifiedComplexObjectWithStrictOmit = StrictOmit<ComplexObject, "nested" | "simple" | "nonexistent">;
    
    // Result: error
    // Type '"simple" | "nested" | "nonexistent"' does not satisfy the constraint '"simple" | "nested"'.
    // Type '"nonexistent"' is not assignable to type '"simple" | "nested"'.
    
    type SimplifiedComplexObjectWithOmit = Omit<ComplexObject, "nested" | "simple" | "nonexistent">;
    
    // Result: no error

    As is shown in the example, StrictOmit ensures that no extra key is specified in the filter.

    DeepOmit

    Recursively omit deep properties according to key names.

    Here is the Teacher interface.

    interface Teacher {
      name: string;
      gender: string;
      students: { name: string; score: number }[];
    }

    Now suppose you want to omit gender property of Teacher, and score property of students. You can achieve this with a simple type filter.

    In the filter, the properties to be omitted completely should be defined as never. For the properties you want to partially omit, you should recursively define the sub-properties to be omitted.

    type TeacherSimple = DeepOmit<
      Teacher,
      {
        gender: never;
        students: {
          score: never;
        };
      }
    >;
    
    // The result will be:
    // {
    //  name: string,
    //  students: {name: string}[]
    // }

    NOTE

    • DeepOmit works fine with Arrays and Sets. When applied to a Map, the filter is only applied to its value.
    • If there exists any property in the filter which is not in the original type, an error will occur.

    OmitProperties

    keywords: filter, props

    Removes all properties extending type P in type T. NOTE: it works opposite to filtering.

    interface Example {
      log(): void;
      version: string;
    }
    
    type ExampleWithoutMethods = OmitProperties<Example, Function>;
    
    // Result:
    // {
    //   version: string;
    // }
    
    // if you want to Omit multiple properties just use union type like
    
    type ExampleWithoutMethods = OmitProperties<Example, Function | string>;
    // Result:
    // { } (empty type)

    PickProperties

    Pick only properties extending type P in type T.

    interface Example {
      log(): void;
      version: string;
      versionNumber: number;
    }
    
    type ExampleOnlyMethods = PickProperties<Example, Function>;
    
    // Result:
    // {
    //   log(): void;
    // }
    
    // if you want to pick multiple properties just use union type like
    
    type ExampleOnlyMethodsAndString = PickProperties<Example, Function | string>;
    // Result:
    // {
    //   log(): void;
    //   version: string;
    // }

    NonNever

    Useful for purifying object types. It improves intellisense but also allows for extracting keys satisfying a conditional type.

    type GetDefined<TypesMap extends { [key: string]: any }> = keyof NonNever<
      { [T in keyof TypesMap]: TypesMap[T] extends undefined ? never : TypesMap[T] }
    >;

    NonEmptyObject

    Useful for accepting only objects with keys, great after a filter like OmitProperties or PickProperties.

    /* return never if the object doesn't have any number value*/
    type NumberDictionary<T> = NonEmptyObject<PickProperties<T, number>>;
    
    // return { a: number }
    type SomeObject = NumberDictionary<{ a: number; b: string }>;
    
    // return never
    type EmptyObject = NumberDictionary<{}>;

    Merge

    keywords: override

    type Foo = {
      a: number;
      b: string;
    };
    
    type Bar = {
      b: number;
    };
    
    const xyz: Merge<Foo, Bar> = { a: 4, b: 2 };
    // Result:
    // {
    //   a: number,
    //   b: number,
    // }

    MarkRequired

    Useful when you're sure some optional properties will be set. A real life example: when selecting an object with its related entities from an ORM.

    class User {
      id: number;
      posts?: Post[];
      photos?: Photo[];
    }
    type UserWithPosts = MarkRequired<User, "posts">;
    
    // example usage with a TypeORM repository -- `posts` are now required, `photos` are still optional
    async function getUserWithPosts(id: number): Promise<UserWithPosts> {
      return userRepo.findOneOrFail({ id }, { relations: ["posts"] }) as Promise<UserWithPosts>;
    }

    MarkOptional

    Useful when you want to make some properties optional without creating a separate type.

    interface User {
      id: number;
      name: string;
      email: string;
      password: string;
    }
    
    type UserWithoutPassword = MarkOptional<User, "password">;
    
    // Result:
    
    // {
    //   id: number;
    //   name: string;
    //   email: string;
    //   password?: string;
    // }

    ReadonlyKeys

    Gets keys of an object which are readonly.

    type T = {
      readonly a: number;
      b: string;
    };
    type Result = ReadonlyKeys<T>;
    // Result:
    // "a"

    WritableKeys

    Gets keys of an object which are writable.

    type T = {
      readonly a: number;
      b: string;
    };
    type Result = WritableKeys<T>;
    // Result:
    // "b"

    OptionalKeys

    Gets keys of an object which are optional.

    type T = {
      a: number;
      b?: string;
      c: string | undefined;
      d?: string;
    };
    type Result = OptionalKeys<T>;
    // Result:
    // "b" | "d"

    RequiredKeys

    Gets keys of an object which are required.

    type T = {
      a: number;
      b?: string;
      c: string | undefined;
      d?: string;
    };
    type Result = RequiredKeys<T>;
    // Result:
    // "a" | "c"

    PickKeys

    Gets keys of properties of given type in object type.

    type T = {
      a: number;
      b?: string;
      c: string | undefined;
      d: string;
    };
    type Result1 = PickKeys<T, string>;
    // Result1:
    // "d"
    type Result2 = PickKeys<T, string | undefined>;
    // Result2:
    // "b" | "c" | "d"

    UnionToIntersection

    Useful for converting mapped types with function values to intersection type (so in this case - overloaded function).

    type Foo = {
      bar: string;
      xyz: number;
    };
    
    type Fn = UnionToIntersection<{ [K in keyof Foo]: (type: K, arg: Foo[K]) => any }[keyof Foo]>;

    Opaque types

    Opaque types allow you to create unique type that can't be assigned to base type by accident. Good examples of opaque types include:

    • JWTs or other tokens - these are special kinds of string used for authorization purposes. If your app uses multiple types of tokens each should be a separate opaque type to avoid confusion.
    • specific currencies - amount of different currencies shouldn't be mixed
    • bitcoin address - special kind of string

    It's critical to understand that each token (second argument to Opaque) has to be unique across your codebase.

    We encourage you to leverage a pattern where you have single function to validate base type and create opaque type.

    type PositiveNumber = Opaque<number, "PositiveNumber">;
    function makePositiveNumber(n: number): PositiveNumber {
      if (n <= 0) {
        throw new Error(`Value ${n} is not positive !`);
      }
      return (n as any) as PositiveNumber; // this ugly cast is required but only when "producing" opaque types
    }
    
    type NegativeNumber = Opaque<number, "NegativeNumber">;
    function makeNegativeNumber(n: number): NegativeNumber {
      if (n >= 0) {
        throw new Error(`Value ${n} is not negative !`);
      }
      return (n as any) as NegativeNumber; // this ugly cast is required but only when "producing" opaque types
    }
    
    let a = makePositiveNumber(5); // runtime check
    let b = makeNegativeNumber(-10); // runtime check
    
    a = b; // error at compile time

    Tuple constraint

    function foo<T extends Tuple>(tuple: T): T {
      return tuple;
    }
    
    const ret = foo(["s", 1]);
    // return type of [string, number]

    You can also parametrize Tuple type with a type argument to constraint it to certain types, i.e. Tuple<string | number>.

    Exhaustive switch cases

    function actOnDummyOptions(options: DummyOptions): string {
      switch (options) {
        case "open":
          return "it's open!";
        case "closed":
          return "it's closed";
        case "unknown":
          return "i have no idea";
        default:
          // if you would add another option to DummyOptions, you'll get error here!
          throw new UnreachableCaseError(options);
      }
    }

    ValueOf type

    const obj = {
      id: "123e4567-e89b-12d3-a456-426655440000",
      name: "Test object",
      timestamp: 1548768231486,
    };
    
    type objKeys = ValueOf<typeof obj>;
    // Result: string | number

    ElementOf type

    const array = [1, 2, true, false];
    type arrayElement = ElementOf<typeof array>;
    // Result: number | boolean

    AsyncOrSync type

    Useful as a return type in interfaces or abstract classes with missing implementation

    interface CiProvider {
      getSHA(): AsyncOrSync<string>;
      // same as
      getSHA(): Promise<string> | string;
    }
    
    class Circle implements CiProvider {
      // implementation can use sync version
      getSHA() {
        return "abc";
      }
    }
    
    class Travis implements CiProvider {
      // implementation can use async version when needed
      async getSHA() {
        // do async call
        return "def";
      }
    }
    
    // to get original type use AsyncOrSyncType
    AsyncOrSyncType<AsyncOrSync<number>> // return 'number'

    Awaited type

    Unwrap promised type:

    Awaited<Promise<number>> // number

    Newable

    keywords: constructor, class

    Type useful when working with classes (not their instances).

    class TestCls {
      constructor(arg1: string) {}
    }
    
    const t1: Newable<any> = TestCls;

    Assertions

    keywords: invariant

    Simple runtime assertion that narrows involved types using assertion functions.

    Note: This function is not purely type level and leaves minimal runtime trace in generated code.

    const something: string | undefined = "abc" as any;
    assert(something, "Something has to be defined!");
    // from now on `something` is string, if this wouldn't be a case, assert would throw
    
    const anything = "abc" as any;
    assert(anything instanceof String, "anything has to be a string!");
    // from now on `anything` is string

    Exact

    keywords: same, equals, equality

    Exact<TYPE, SHAPE> Checks if TYPE is exactly the same as SHAPE, if yes than TYPE is returned otherwise never.

    type ABC = { a: number; b: number; c: number }
    type BC = { b: number; c: number }
    type C = { c: number }
    
    Exact<ABC, C> // returns NEVER
    Exact<C, C> // returns C

    XOR

    Gets the XOR (Exclusive-OR) type which could make 2 types exclude each other.

    type A = { a: string };
    type B = { a: number; b: boolean };
    type C = { c: number };
    
    let A_XOR_B: XOR<A, B>;
    let A_XOR_C: XOR<A, C>;
    
    // fail
    A_XOR_B = { a: 0 };
    A_XOR_B = { b: true };
    A_XOR_B = { a: "", b: true };
    A_XOR_C = { a: "", c: 0 }; // would be allowed with `A | C` type
    
    // ok
    A_XOR_B = { a: 0, b: true };
    A_XOR_B = { a: "" };
    A_XOR_C = { c: 0 };

    Functional type essentials

    Head & Tail: useful for functional programming, or as building blocks for more complex functional types.

    function tail<T extends any[]>(array: T): Tail<T> {
      return array.slice(1) as Tail<T>;
    }
    
    type FirstParameter<FnT extends (...args: any) => any> = FnT extends (...args: infer ArgsT) => any
      ? Head<ArgsT>
      : never;

    Contributors

    Thanks goes to these wonderful people (emoji key):


    Chris Kaczor

    💻 💼 💡 📖

    Xiao Liang

    💻 🤔 📖

    Mateusz Burzyński

    💻 🤔 📖

    Maciej Bembenista

    💻 🤔 📖

    Michael Tontchev

    💻 🤔 📖

    Thomas den Hollander

    💻 🤔 📖

    Esa-Matti Suuronen

    💻 🤔 📖

    Ilya Semenov

    💻 🤔 📖

    Code Checks

    👀

    Patricio Palladino

    🤔

    Artur Kozak

    💻 🤔 📖 👀

    Zihua Wu

    💻 🤔 📖

    Kevin Peno

    💻

    Dom Parfitt

    🤔

    EduardoRFS

    💻 📖

    Andrew C. Dvorak

    📖

    Adam Russell

    💻 📖

    Piotr Szlachciak

    💻 🤔 📖

    Mikhail Swift

    💻

    Ryan Zhang

    💻 🤔 📖

    Francesco Borzì

    📖 💻

    Marnick L'Eau

    💻 🤔 📖

    kubk

    💻

    Bill Barry

    💻 📖

    Andrzej Wódkiewicz

    💻 📖 🤔

    Christian

    🤔

    Matthew Leffler

    📖

    studds

    💻

    This project follows the all-contributors specification. Contributions of any kind welcome! Read more

    Install

    npm i ts-essentials

    DownloadsWeekly Downloads

    1,952,433

    Version

    7.0.2

    License

    MIT

    Unpacked Size

    68.1 kB

    Total Files

    9

    Last publish

    Collaborators

    • avatar