All essential TypeScript types in one place 🤙
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.
- Install
- What's inside?
- Basic
- Dictionaries
- Deep Partial & Deep Required & Deep Readonly & Deep NonNullable
- Writable
- Omit
- StrictOmit
- DeepOmit
- OmitProperties
- PickProperties
- NonNever
- Merge
- MarkRequired
- ReadonlyKeys
- WritableKeys
- UnionToIntersection
- Opaque types
- Tuple constraint
- Literal types
- Exhaustive switch cases
- ValueOf type
- AsyncOrSync type
- Contributors
Primitive
type matching all primitive values.
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
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'
}
}
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;
Our version of Omit
is renamed to StrictOmit
in v3
, since the builtin Omit
has become part of TypeScript 3.5
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)
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.
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 withArray
s andSet
s. When applied to aMap
, 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.
Removes all properties extending type P
in type T
.
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)
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;
// }
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] }
>;
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<{}>;
type Foo = {
a: number;
b: string;
};
type Bar = {
b: number;
};
const xyz: Merge<Foo, Bar> = { a: 4, b: 2 };
// Result:
// {
// a: number,
// b: number,
// }
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>;
}
Gets keys of an object which are readonly.
type T = {
readonly a: number;
b: string;
};
type Result = ReadonlyKeys<T>
// Result:
// "a"
Gets keys of an object which are writable.
type T = {
readonly a: number;
b: string;
};
type Result = WritableKeys<T>
// Result:
// "b"
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]>;
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; // this ugly cast is required but only when "producing" opaque types
}
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>
.
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.
// 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
};
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);
}
}
const obj = {
id: "123e4567-e89b-12d3-a456-426655440000",
name: "Test object",
timestamp: 1548768231486,
};
type objKeys = ValueOf<typeof obj>;
// Result: string | number
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";
}
}
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome! Read more