ts-essentials
All essential TypeScript types in one place 🤙
## 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)):
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! [Read more](./CONTRIBUTING.md)