ts-essentials
All essential TypeScript types in one place 🤙
Install
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?
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 & Deep NonNullable
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}],
}
}
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;
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.
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">;
type SimplifiedComplexObject = StrictOmit<ComplexObject, "nested" | "simple">;
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">;
type SimplifiedComplexObjectWithOmit = Omit<ComplexObject, "nested" | "simple" | "nonexistent">;
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,
}
}>
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
.
interface Example {
log(): void;
version: string;
}
type ExampleWithoutMethods = OmitProperties<Example, Function>;
type ExampleWithoutMethods = OmitProperties<Example, Function | string>;
PickProperties
Pick only properties extending type P
in type T
.
interface Example {
log(): void;
version: string;
versionNumber: number;
}
type ExampleOnlyMethods = PickProperties<Example, Function>;
type ExampleOnlyMethodsAndString = PickProperties<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] }
>;
NonEmptyObject
Useful for accepting only objects with keys, great after a filter like OmitProperties or PickProperties.
type NumberDictionary<T> = NonEmptyObject<PickProperties<T, number>>;
type SomeObject = NumberDictionary<{ a: number, b: string }>;
type EmptyObject = NumberDictionary<{}>;
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>;
}
ReadonlyKeys
Gets keys of an object which are readonly.
type T = {
readonly a: number;
b: string;
};
type Result = ReadonlyKeys<T>
WritableKeys
Gets keys of an object which are writable.
type T = {
readonly a: number;
b: string;
};
type Result = WritableKeys<T>
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.
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 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