What is ts-essentials?
The ts-essentials package provides a set of TypeScript types to enhance the TypeScript typing experience, offering more strict and powerful type definitions. It includes utility types, type guards, and other helpers that are not available in the standard TypeScript library.
What are ts-essentials's main functionalities?
DeepReadonly
Makes all properties of an object type recursively readonly. It is useful for defining immutable state or configurations.
type MyObject = { a: { b: { c: number } } };\nconst readonlyObject: DeepReadonly<MyObject> = { a: { b: { c: 1 } } };\n// readonlyObject.a.b.c = 2; // Error: Cannot assign to 'c' because it is a read-only property.
Writable
Converts a readonly object into a writable one, removing the readonly modifier from all properties.
type MyReadOnlyObject = { readonly a: number };\nconst writable: Writable<MyReadOnlyObject> = { a: 1 };\nwritable.a = 2; // No error, 'a' is writable.
StrictOmit
Creates a type by omitting the keys provided from the given object type, ensuring that only keys that exist on the type are specified.
interface MyObject { a: number; b: string; c: boolean; }\nconst myObject: StrictOmit<MyObject, 'a' | 'b'> = { c: true };\n// myObject.a or myObject.b does not exist.
LiteralUnion
Allows for a union type to include specific literal types as well as additional types, typically used for string literals with an escape hatch for other values.
type Direction = LiteralUnion<'left' | 'right', string>;\nconst direction: Direction = 'any string'; // No error, can be 'left', 'right', or any other string.
MarkRequired
Marks certain properties of an object type as required, changing them from optional to mandatory.
type MyObject = { a?: number; b: string; }\nconst required: MarkRequired<MyObject, 'a'> = { a: 1, b: 'string' };\n// Property 'a' is required in 'required'.
Other packages similar to ts-essentials
utility-types
Provides a collection of utility types for TypeScript, similar to ts-essentials. It includes types for operations like picking, omitting, and mapping properties of object types.
type-fest
A comprehensive library of utility types for TypeScript, with a wide range of type helpers. It covers more ground than ts-essentials and is regularly updated with new types.
typesafe-actions
Focused on Redux action creators, typesafe-actions provides utility types and functions for creating and handling actions in a type-safe manner, which is a more specific use case compared to the general utilities provided by ts-essentials.
ts-essentials
All essential TypeScript types in one place 🤙
Install
npm add --save-dev ts-essentials
or
yarn add --dev ts-essentials
What's inside?
Basic:
Primitive
type matching all primitive values.
Dictionaries
const stringDict: Dictionary<string> = {
a: "A",
b: "B",
};
const dictOfNumbers: Dictionary<string, number> = {
420: "four twenty",
1337: "HAX",
};
export type DummyOptions = "open" | "closed" | "unknown";
const dictFromUnionType: Dictionary<number, DummyOptions> = {
closed: 1,
open: 2,
unknown: 3,
};
type stringDictValues = DictionaryValues<typeof stringDict>;
Deep Partial & Deep Required & Deep Readonly
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: [],
},
};
type ComplexObjectReadonly = DeepReadonly<ComplexObject>;
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,
},
},
];
test[0].foo = "b";
test[0].bar.x = 2;
Omit
type ComplexObject = {
simple: number;
nested: {
a: string;
array: [{ bar: number }];
};
};
type SimplifiedComplexObject = Omit<ComplexObject, "nested">;
type SimplifiedComplexObject = Omit<ComplexObject, "nested" | "simple">;
OmitProperties
Removes all properties extending type P
in type T
.
interface Example {
log(): void;
version: string;
}
type ExampleWithoutMethods = OmitProperties<Example, Function>;
type ExampleWithoutMethods = OmitProperties<Example, Function | 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] }
>;
Merge
type Foo = {
a: number;
b: string;
};
type Bar = {
b: number;
};
const xyz: Merge<Foo, Bar> = { a: 4, b: 2 };
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'>;
async function getUserWithPosts(id: number): Promise<UserWithPosts> {
return userRepo.findOneOrFail({ id }, { relations: ['posts'] }) as Promise<UserWithPosts>;
}
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
type PositiveNumber = Opaque<number, "positive-number">;
function makePositiveNumber(n: number): PositiveNumber {
if (n <= 0) {
throw new Error("Value not positive !!!");
}
return (n as any) as PositiveNumber;
}
Tuple constraint
function foo<T extends Tuple>(tuple: T): T {
return tuple;
}
const ret = foo(["s", 1]);
You can also parametrize Tuple
type with a type argument to constraint it to certain types, i.e.
Tuple<string | number>
.
Literal types
For TypeScript >= 3.4: TypeScript 3.4 shipped
const
assertions which are very
similar to our literal
helper but also make type readonly, you should prefer as const
construct.
For TypeScript < 3.4: this is served as a backport of the const
assertions added since TypeScript 3.4.
const t = {
letter: literal("a"),
digit: literal(5),
};
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:
throw new UnreachableCaseError(options);
}
}
ValueOf type
const obj = {
id: "123e4567-e89b-12d3-a456-426655440000",
name: "Test object",
timestamp: 1548768231486,
};
type objKeys = ValueOf<typeof obj>;
AsyncOrSync type
Useful as a return type in interfaces or abstract classes with missing implementation
interface CiProvider {
getSHA(): AsyncOrSync<string>;
getSHA(): Promise<string> | string;
}
class Circle implements CiProvider {
getSHA() {
return "abc";
}
}
class Travis implements CiProvider {
async getSHA() {
return "def";
}
}
Contributors
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome! Read more