ts-essentials

ts-essentials

All essential TypeScript types in one place 🤙

Downloads Build status Software License All Contributors codechecks.io

## Install ```sh npm install --save-dev ts-essentials ``` 👉 We require `typescript>=3.5`. If you're looking for support for older TS versions use `ts-essentials@2` instead. ## What's inside? - [Install](#Install) - [What's inside?](#Whats-inside) - [Basic](#Basic) - [Dictionaries](#Dictionaries) - [Deep Partial & Deep Required & Deep Readonly & Deep NonNullable](#Deep-Partial--Deep-Required--Deep-Readonly--Deep-NonNullable) - [Writable](#Writable) - [Buildable](#Buildable) - [Omit](#Omit) - [StrictOmit](#StrictOmit) - [Comparison between `Omit` and `StrictOmit`](#Comparison-between-Omit-and-StrictOmit) - [DeepOmit](#DeepOmit) - [OmitProperties](#OmitProperties) - [PickProperties](#PickProperties) - [NonNever](#NonNever) - [Merge](#Merge) - [MarkRequired](#MarkRequired) - [MarkOptional](#MarkOptional) - [ReadonlyKeys](#ReadonlyKeys) - [WritableKeys](#WritableKeys) - [UnionToIntersection](#UnionToIntersection) - [Opaque types](#Opaque-types) - [Tuple constraint](#Tuple-constraint) - [Literal types](#Literal-types) - [Exhaustive switch cases](#Exhaustive-switch-cases) - [ValueOf type](#ValueOf-type) - [AsyncOrSync type](#AsyncOrSync-type) - [Contributors](#Contributors) ### Basic - `Primitive` type matching all primitive values. ### Dictionaries ```typescript const stringDict: Dictionary = { a: "A", b: "B", }; // Specify second type argument to change dictionary keys type const dictOfNumbers: Dictionary = { 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 = { closed: 1, open: 2, unknown: 3, }; // and get dictionary values type stringDictValues = DictionaryValues; // Result: string ``` ### Deep Partial & Deep Required & Deep Readonly & Deep NonNullable ```typescript type ComplexObject = { simple: number; nested: { a: string; array: [{ bar: number }]; }; }; type ComplexObjectPartial = DeepPartial; const samplePartial: ComplexObjectPartial = { nested: { array: [{}], }, }; type ComplexObjectAgain = DeepRequired; const sampleRequired: ComplexObjectAgain = { simple: 5, nested: { a: "test", array: [{bar: 1}], }, }; type ComplexObjectReadonly = DeepReadonly; type ComplexNullableObject = { simple: number | null | undefined; nested: { a: string | null | undefined; array: [{ bar: number | null | undefined }] | null | undefined; }; }; type ComplexObjectNonNullable = DeepNonNullable; const sampleNonNullable: ComplexObjectNonNullable = { simple: 5, nested: { a: "test", array: [{bar: null}], // Error: Type 'null' is not assignable to type 'number' } } ``` ### Writable Make all attributes of object writable. ```typescript type Foo = { readonly a: number; readonly b: string; }; const foo: Foo = ({ a: 1, b: "b" }(foo as Writable).a = 42); ``` ```typescript type Foo = { readonly foo: string; bar: { readonly x: number; }; }[]; const test: DeepWritable = [ { foo: "a", bar: { x: 5, }, }, ]; // we can freely write to this object test[0].foo = "b"; test[0].bar.x = 2; ``` ### Buildable 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. ```typescript interface ReadonlyObject extends Readonly<{ simple: number; nested: Readonly<{ a: string; array: ReadonlyArray>; }>; }> {} const buildable: Buildable = {}; 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. ```typescript type ComplexObject = { simple: number; nested: { a: string; array: [{ bar: number }]; }; }; type SimplifiedComplexObject = StrictOmit; // Result: // { // simple: number // } // if you want to Omit multiple properties just use union type: type SimplifiedComplexObject = StrictOmit; // Result: // { } (empty type) ``` #### Comparison between `Omit` and `StrictOmit` Following the code above, we can compare the behavior of `Omit` and `StrictOmit`. ```typescript type SimplifiedComplexObjectWithStrictOmit = StrictOmit; // 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; // 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. ```typescript 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. ```typescript type TeacherSimple = DeepOmit // The result will be: // { // name: string, // students: {name: string}[] // } ``` NOTE - `DeepOmit` works fine with `Array`s and `Set`s. 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 Removes all properties extending type `P` in type `T`. ```typescript interface Example { log(): void; version: string; } type ExampleWithoutMethods = OmitProperties; // Result: // { // version: string; // } // if you want to Omit multiple properties just use union type like type ExampleWithoutMethods = OmitProperties; // Result: // { } (empty type) ``` ### PickProperties Pick only properties extending type `P` in type `T`. ```typescript interface Example { log(): void; version: string; versionNumber: number; } type ExampleOnlyMethods = PickProperties; // Result: // { // log(): void; // } // if you want to pick multiple properties just use union type like type ExampleOnlyMethodsAndString = PickProperties; // Result: // { // log(): void; // version: string; // } ``` ### NonNever Useful for purifying object types. It improves intellisense but also allows for extracting keys satisfying a conditional type. ```typescript type GetDefined = 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. ```typescript /* return never if the object doesn't have any number value*/ type NumberDictionary = NonEmptyObject>; // return { a: number } type SomeObject = NumberDictionary<{ a: number, b: string }>; // return never type EmptyObject = NumberDictionary<{}>; ``` ### Merge ```typescript type Foo = { a: number; b: string; }; type Bar = { b: number; }; const xyz: Merge = { 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. ```typescript class User { id: number; posts?: Post[]; photos?: Photo[]; } type UserWithPosts = MarkRequired; // example usage with a TypeORM repository -- `posts` are now required, `photos` are still optional async function getUserWithPosts(id: number): Promise { return userRepo.findOneOrFail({ id }, { relations: ['posts'] }) as Promise; } ``` ### MarkOptional Useful when you want to make some properties optional without creating a separate type. ```typescript interface User { id: number; name: string; email: string; password: string; } type UserWithoutPassword = MarkOptional; // Result: // { // id: number; // name: string; // email: string; // password?: string; // } ``` ### ReadonlyKeys Gets keys of an object which are readonly. ```typescript type T = { readonly a: number; b: string; }; type Result = ReadonlyKeys // Result: // "a" ``` ### WritableKeys Gets keys of an object which are writable. ```typescript type T = { readonly a: number; b: string; }; type Result = WritableKeys // Result: // "b" ``` ### UnionToIntersection Useful for converting mapped types with function values to intersection type (so in this case - overloaded function). ```typescript type Foo = { bar: string; xyz: number; }; type Fn = UnionToIntersection<{ [K in keyof Foo]: (type: K, arg: Foo[K]) => any }[keyof Foo]>; ``` ### Opaque types ```typescript type PositiveNumber = Opaque; function makePositiveNumber(n: number): PositiveNumber { if (n <= 0) { throw new Error("Value not positive !!!"); } return (n as any) as PositiveNumber; // this ugly cast is required but only when "producing" opaque types } ``` ### Tuple constraint ```typescript function foo(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`. ### Literal types _For TypeScript >= 3.4_: TypeScript 3.4 shipped [`const` assertions](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html) which are very similar to our `literal` helper but also make type readonly, you should prefer `as const` construct. `literal` is deprecated tn `ts-essentials` 3.x, which requires TypeScript >=3.5. _For TypeScript < 3.4_: this is served as a backport of the [`const` assertions](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html) added since TypeScript 3.4. ```typescript // prevent type widening https://blog.mariusschulz.com/2017/02/04/typescript-2-1-literal-type-widening const t = { letter: literal("a"), // type stays "a" not string digit: literal(5), // type stays 5 not number }; ``` ### Exhaustive switch cases ```typescript 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 ```typescript const obj = { id: "123e4567-e89b-12d3-a456-426655440000", name: "Test object", timestamp: 1548768231486, }; type objKeys = ValueOf; // Result: string | number ``` ### AsyncOrSync type Useful as a return type in interfaces or abstract classes with missing implementation ```typescript interface CiProvider { getSHA(): AsyncOrSync; // same as getSHA(): Promise | 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"; } } ``` ## Contributors Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
Chris Kaczor
Chris Kaczor

💻 💼 💡 📖
Xiao Liang
Xiao Liang

💻 🤔 📖
Mateusz Burzyński
Mateusz Burzyński

💻 🤔 📖
Maciej Bembenista
Maciej Bembenista

💻 🤔 📖
Michael Tontchev
Michael Tontchev

💻 🤔 📖
Thomas den Hollander
Thomas den Hollander

💻 🤔 📖
Esa-Matti Suuronen
Esa-Matti Suuronen

💻 🤔 📖
Ilya Semenov
Ilya Semenov

💻 🤔 📖
Code Checks
Code Checks

👀
Patricio Palladino
Patricio Palladino

🤔
Artur Kozak
Artur Kozak

💻 🤔 📖 👀
Zihua Wu
Zihua Wu

💻 🤔 📖
Kevin Peno
Kevin Peno

💻
Dom Parfitt
Dom Parfitt

🤔
EduardoRFS
EduardoRFS

💻 📖
Andrew C. Dvorak
Andrew C. Dvorak

📖
Adam Russell
Adam Russell

💻 📖
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! [Read more](./CONTRIBUTING.md)