This library is a handy collection of TypeScript plain and generic type definitions and interfaces for both frontend and backend. You may expect zero runtime overhead if you use imported items only in type contexts.
Generated by typedoc
This project was inspired by 'ts-essentials' library. Some type names were taken from them.
The most convenient way to explore 'ts-typedefs'
API is by easily browsing your editor's completion
list that shows signatures and descriptions for selected items. Types, functions, and classes names are intended to be super descriptive and intuitive.
All functional units provide typedoc
documentation in comments so it is easy for
IDEs to provide you with good hints.
import { DeepPartial, FilterProps, Func, Op } from 'ts-typedefs';
class User { /* ... */ }
type UserData = FilterProps<User, Op.NotExtends<Func>>;
function updateUser(userUpd: DeepPartial<UserData>) { /* ... */ }
Let's see them in details.
Defines an object with keys of type TKeys
, and all values of TValue
type.
type t0 = Obj; // { [key: string]: any; }
type t1 = Obj<boolean>; // { [key: string]: boolean; }
type t2 = Obj<string, number>; // { [key: number]: string; }
type t3 = Obj<number, 'p1' | 'p2'>; // { p1: number, p2: number; }
Defines constructor function type that instantiates TInstance
and accepts arguments of TArgs
type.
interface User { /* ... */ }
// new (...args: any) => User
type t0 = Class<User>;
// new (...args: [string, number]) => User
type t1 = Class<User, [string, number]>;
Defines an instance type of the given class or the type of its prototype
property.
TClass
may even be an abstract class, though those ones are not newable,
but the type of their instances can be obtained through their prototype
property.
import { InstanceType } from 'ts-typedefs';
class User { id!: number; } // plain class
function getAbstractUser() {
abstract class AbstractUser { // local abstract class, its type is not
id!: number; // accessible in the global namespace
}
return AbstractUser;
}
type t0 = InstanceType<typeof User>; // User
type t1 = InstanceType<ReturnType<typeof getAbstractUser>>; // AbstractUser
Defines a union type of all the values stored in TObj
.
interface User {
id: number;
login: string | null;
password: string;
isDisabled: boolean;
}
/* number | string | null | boolean */
type t0 = ValueOf<User>;
/* union type of all properties and methods of `Array<boolean>` */
type t1 = ValueOf<boolean[]>;
Defines the same object type as TSrcObj
, but without TKeysUnion
keys.
interface User {
id: number;
login: string | null;
password: string;
isDisabled: boolean;
}
/*
{
id: number;
login: string | null;
isDisabled: boolean;
}
*/
type t0 = RemoveKeys<User, 'password'>;
/* { id: number; } */
type t1 = RemoveKeys<User, 'password' | 'isDisabled' | 'login'>;
Defines the same type as TObj
but with particular properties filtered out according to TApproveCond
. TApproveCond
is a boolean operator tree structure that defines the criteria that the filtered values must match. All these operators are defined in Op
namespace.
interface User {
id: number;
login: string | null;
password: string;
isDisabled: boolean;
flag: boolean;
}
/* { login: string; } */
type t0 = FilterProps<User, Op.Extends<string>>;
/*
{
isDisabled: boolean;
flag: boolean;
}
*/
type t0 = FilterProps<User, Op.Extends<boolean>>;
/*
{
isDisabled: boolean;
flag: boolean;
id: number;
}
*/
type t1 = FilterProps<
User,
Op.And<[
Op.Not<Op.UnionIncludes<string>>, // Op.UnionExcludes<> is another option
Op.Nand<[false, true, true, true]> // this condition is always true
]>
>;
Because of some TypeScript limitations and bugs TApproveCond
tree must be not more than 5 levels deep (number of levels limitation may change, but it can only become greater).
Defines the same object type as TSrcObj
, but all values of TMappedValue
type.
DeepMapValues<>
variant maps values for all nested objects recursively.
interface User {
login?: string | null;
friend: {
friendliness: number;
ref: User;
}
}
/* { login: boolean; friend: boolean; } */
type t0 = MapValues<User, boolean>;
/*
{
login: boolean;
friend: {
friendliness: boolean;
ref: DeepMapValues<User, boolean>
}
}
*/
type t1 = DeepMapValues<User, boolean>;
Merge objects TObj1
and TObj2
.
Properties types from TObj2
override the ones defined on TObj1
.
This type is analogous to the return type of Object.assign()
interface O1 {
p1: number;
p2: string;
p3: boolean;
}
interface O2 {
p2: number | null;
p3: string;
p4: O1;
}
/*
{
p1: number;
p2: number | null;
p3: string;
p4: O1;
}
*/
type t0 = Merge<O1, O2>;
/*
{
p1: number;
p2: string;
p3: boolean;
p4: O1;
}
*/
type t1 = Merge<O2, O1>;
Partial/Required<TObj, TKeys = keyof TObj>
defines the same type as TObj
but
with all TKeys
made optional/required.
DeepPartial/Required<>
defines the same type as TObj
but with all properties made recursively Partial/Required<>
.
This two types are actually exactly opposite to each other.
interface User {
id: number;
name: {
first: string;
}
}
/*
{
id?: undefined | number;
name?: undefined | {
first: string;
}
}
*/
type PartUser = Partial<User, /* TKeys = keyof User */>;
/*
{
id?: number | undefined;
name?: undefined | {
first?: string | undefined;
};
}
*/
type DeepPartUser = DeepPartial<User>;
type RequUser = Required<User, /* TKeys = keyof User */>; // User
type DeepRequUser = DeepRequired<DeepPartUser>; // User
Readonly/Mutable<TObj, TKeys = keyof TObj>
defines the same type as TObj
but
with all TKeys
made readonly/mutable.
DeepReadonly/Mutable<>
defines the same type as TObj
but with all properties made recursively Readonly/Mutable<>
.
This two types are actually exactly opposite to each other.
interface User {
id: number;
name: {
first: string;
}
}
/*
{
readonly id: number;
readonly name: {
first: string;
}
}
*/
type RoUser = Readonly<User, /* TKeys = keyof User */>;
/*
{
readonly id: number;
readonly name: {
readonly first: string;
};
}
*/
type DeepRoUser = DeepReadonly<User>;
type MutUser = Mutable<RoUser, /* TKeys = keyof User */>; // User
type DeepMutUser = DeepMutable<DeepRoUser>; // User
DeepReadonly<>
is quite handy when you define deep readonly multidimensional arrays.
type t0 = DeepReadonly<number[][][]>;
// readonly (readonly (readonly number[])[])[]
Defines the same type as TObj
, but adds 'optional' modifier ?
to all
properties that allow undefined
as their value type (this includes unknown
and any
).
interface User {
bio: string | undefined;
secret: unknown;
name: string;
}
/*
{
bio?: string | undefined;
secret?: unknown;
name: string; // notice may-not-be `undefined` props don't get '?' modifier
}
*/
type RepairedUser = OptionalLikelyUndefProps<User>;
Defines a Function subtype with the given arguments, return type and this
context. If it is AsyncFunc<>
TRetval
is packed into Promise<TRetval>
interface User { /* ... */ }
// (this: any, ...args: any) => unknown
type t0 = Func;
// (this: any, ...args: [string, number | undefined]) => unknown
type t1 = Func<[string, number | undefined]>;
// (this: any, ...args: [boolean]) => void
type t2 = Func<[boolean], void>;
// (this: User, ...args: [boolean]) => number
type t3 = Func<[boolean], number, User>;
// (this: any, ...args: [string]) => Promise<User>
type t4 = AsyncFunc<[string], User>
Defines the unpacked result type of the Promise
returned by the specified TAsyncFunc
.
class User {
static async getById(id: number): Promise<User> {
// ...
}
}
// User
type t0 = AsyncFuncReturnType<AsyncFunc<[number], User>>;
// User
type t1 = AsyncFuncReturnType<typeof User.getById>
Defines a static or instance method decorator function type. TArgs
tuple type limits the arguments' type decorated method accepts, TRetval
limits the return type of the decorated method. TMethNameLimit
defines the limitation for method names this decorator may be applied to.
declare function decor_any(): MethodDecorator;
declare function decor_bool(): MethodDecorator<[boolean]>;
declare function decor_getId(): MethodDecorator<any[], any, 'getId'>;
function decor_str$num_bool(): MethodDecorator<[string, number], boolean> {
// argument types are automatically deduced and strongly typed here
return (protoOrClass, methodName, propDescriptor) => {
/* (this: typeof protoOrClass, ...args: [string, number]) => boolean */
const val = propDescriptor.value;
// ...
};
};
class User {
@decor_getId() // compile error (method name mismatch)
@decor_bool() // compile error (params type mismatch)
@decor_str$num_bool() // compile error (params and return type mismatch)
@decor_any() // works fine
meth0(bol: boolean, num: number): void {}
@decor_any() // works fine
@decor_bool() // works fine
meth1(bol: boolean) {
return bol ? 32 : 'kek';
}
@decor_any() // works fine
@decor_str$num_bool() // works fine
meth2(str: string, num: number) {
return !!str && !!num;
}
@decor_getId() // works fine
getId() { }
}
Defines a static or instance property decorator function type.
declare function decorateAny(): PropertyDecorator;
function decorateStr(): PropertyDecorator<string> {
return /* function arguments are analogously deduced */;
};
function decorIdProp(): PropertyDecorator<number, 'id' | '_id'> {
return /* function arguments are analogously deduced */;
};
export class User {
@decorIdProp() // compile error (prop name and value type mismatch)
@decorateStr() // works fine
@decorateAny() // works fine
prop0!: string;
@decorIdProp() // compile error (prop name mismatch)
@decorateStr() // compile error (prop value type mismatch)
@decorateAny() // works fine
prop1!: number;
@decorIdProp() // works fine
@decorateStr() // compile error (prop value type mismatch)
@decorateAny() // works fine
_id!: number;
@decorIdProp() // compile error (prop value type mismatch)
@decorateStr() // works fine
@decorateAny() // works fine
id!: string;
}
Sequentially performs the following logic:
Expands to TIfTrue
if TCond extends true
.
Expands to TElse
if TCond extends false
.
Expands to TIfCondIsBool
if TCond extends boolean
.
As a convention, enclose TCond
argument in parens.
type t0 = If<(true), number, string>; // number
type t1 = If<(false), number, string>; // string
type t2 = If<(boolean), number, string, bigint>; // bigint
type t3 = If<(And<[NotExtends<22, number>, true, true]>),
string,
If<(false), // nested condition
number,
string
>>; // string
// You may use leading ampersand or pipe in order to explicitly separate branches visually
type t4 = If<(true)
| number, // you may use & instead of |
| string
>; // number
Defines false
unit type if T extends true
.
Defines true
unit type if T extends false
.
Defines TIfTIsBool
when T
is exactly boolean
type.
type t0 = Not<true>; // false
type t1 = Not<false>; // true
type t2 = Not<boolean, number>; // number
type t3 = Not<Not<true>>; // true
Defines true
or false
accroding to the definition of and/nand(negated and)
logical operator.
It gets applied to all the argumets in the given tuple type T
.
type t0 = And<[true, true, true]>; // true
type t1 = And<true[]>; // true
type t2 = And<[true, false, true]> // false
type t3 = And<boolean[]>; // false
type t4 = And<[boolean, true]>; // false
type t5 = Nand<[true, true]>; // false
Defines true
or false
accroding to the definition of or/nor(negated or)
logical operator.
It gets applied to all the argumets in the given tuple type T
.
type t0 = Or<[false, false, false]>; // false
type t1 = Or<false[]>; // false
type t2 = Or<[false, true, false]> // true
type t3 = Or<boolean[]>; // true
type t4 = Or<[boolean, false]>; // true
type t5 = Nor<[true, true]>; // false
Defines true
if TExtender
is assignable to TExtendee
, otherwise false
.
It verifies that you may physically assign a value of type TExtender
to TExtendee
.
That's why union types with excess members that are not assignable to TExtendee
will evaluate to false
.
type t0 = Extends<string | null, string>; // false
type t1 = Extends<true, boolean>; // true
type t2 = Extends<never, never>; // true
type t3 = NotExtends<22, number>; // false
Defines true
if T1
is exactly T2
, false
otherwise.
Even AreSame<unknown, any>
expands to false
. Only the same types expand to true
.
It doesn't tolerate co/bi/contravaraince, only the types of exactly the same shapes (excluding function types limitation) will cause to return true
.
Beware that this type works as vanilla extends
clause with function types,
so comparing functions is not that strict.
type t0 = AreSame<{}, { num: number }>; // false
type t1 = AreSame<
{ num: number, str: string },
{ num: number, str: string }
>; // true
type t2 = AreSame<any, unknown>; // false
type t8 = AreSame<Func, Func>; // true
type t9 = AreSame<[number], [number]>; // true
type t10 = AreSame<[number, string], [number]>; // false
Defines true[false]
if TSuspect
is exactly of any
type, false[true]
otherwise.
type t0 = IsAny<any>; // true
type t1 = IsAny<unknown>; // false
type t2 = IsAny<never>; // false
type t3 = IsAny<string>; // false
type t4 = IsNotAny<any>; // false
type t5 = IsNotAny<unknown>; // true
// ...
Defines true[false]
if TSuspect
is exactly of unknown
type, false[true]
otherwise.
type t0 = IsUnknown<unknown>; // true
type t1 = IsUnknown<boolean>; // false
type t2 = IsNotUnknown<never>; // true
// ...
C++ style operator, a syntactic sugar for writing casts like
value as any as T
when a simple value as T
cast cannot be performed.
Use it with caution!
This function is actually noop at runtime, all it does is it suppresses
'inability to cast' tsc error. It's better to use this function rather than
value as any as T
cast, because it amplifies your attention to such uneven
places in code and it may be easier to do a Ctrl + F search for these.
interface User {
// ...
}
type UserUpdate = DeepPartial<RemoveKeys<User, 'password'>>;
const userUpd: UserUpdate = // ...
Object.assign(userUpd, {
password: 'somepassword-pfff', otherRequiredFields: // ...
});
// For future devs: reinterpreting here, because userUpd has the same shape as `User`
let user = reinterpret<User>(userUpd);
// `typeof user` is `User`
Class used to perform never
type value checks in unreachable code.
const val: string | number;
if (typeof val === 'string') {
return null;
} else if (typeof val === 'number') {
throw new Debug.UnreachableCodeError(val); // compiler error: val is not of type `never` here
return;
} else {
throw new Debug.UnreachableCodeError(val); // this is ok val has `never` type here
}
enum Enum {
A, B, C
}
let suspect: Enum = // ...
switch (suspect) {
case Enum.A: return;
case Enum.B: return;
default: {
// compiler error, this path is reachable
// as we didn't handle `suspect === Enum.C` case
throw new Debug.UnreachableCodeError(suspect);
}
}
Defines nominal type by adding a property with TTagName
value to TTarget
.
TTagName
must be unique across your application, treat it like the name of
your nominal type.
With this type, you may pick particular subclass of values from the given type and force
your clients to filter other values that are assignable to TTarget
but don't
obey to your prerequisites, thus making them pay more attention to them.
type PositiveInt = Tag<number, 'PositiveInt'>;
type CsvString = Tag<string, 'CsvString'>;
// Prerequisites: `userId > 0`
async function getUser(userId: PositiveInt) {
return userRepository.findById(userId);
}
// Prerequisites: given string must be valid csv
function parseCsv(csvString: CsvString) {
// Here you may be sure that client payed attention to checking the input
const lines = csvString.split('\n').map(line => line.split(','));
}
getUser(-2); // compile error
getUser(58); // compile error
getUser(58 as PositiveInt); // fine (explicit cast pays your attention to prerequisites)
parseCsv('\nbla bla'); // compile error
parseCsv('a,b,c\nd,e,f' as CsvString); // fine
Defines an intersection type of all union's items.
Because of TypeScript boolean representation as type boolean = true | false
you get the following result:
UnionToIntersection<boolean>
is true & false
// string & number & number[]
type t0 = UnionToIntersection<string | number | number[]>;
Defines the type of value, which is passed to TPromise.then(cb)
cb
callback.
type t0 = UnpackPromise<Promise<number>>; // number;
type t1 = UnpackPromise<Promise<void>>; // void;
// Promise<string>;
type t2 = UnpackPromise<
Promise<Promise<string>>
>;
// string
type t3 = UnpackPromise<UnpackPromise<
Promise<Promise<string>>
>>;
Shorthand for Partial/Required/Readonly/Mutable/NullableProps<Pick<TObj, TKeys>>
, but better optimized (into one mapped object type).
interface User {
readonly id: number;
name?: string;
bio: string;
}
PickAsOptional<User, 'name' | 'id'> === Partial<Pick<User, 'name' | 'id'>>
PickAsRequired<User, 'name' | 'id'> === Required<Pick<User, 'name' | 'id'>>
PickAsReadonly<User, 'name' | 'id'> === Readonly<Pick<User, 'name' | 'id'>>
// ...
Nullable<T>
defines type T
that may also be null
or undefined
:
export type Nullable<T> = T | undefined | null;
DeepNullable<T>
defines the same type as TObj
but with all properties made NonNullable<>
recursively.
interface User {
id: number;
name: {
first: string;
last: string;
};
}
/*
Nullable<{
id?: Nullable<number>;
name?: Nullable<{
first?: Nullable<string>;
last?: Nullable<string>;
}>;
}>
*/
type t0 = DeepNullable<User>;
Defines a union of all possible value types defined in the language,
null
and undefined
are considered to be primitive types.
export type Primitive = (
| number
| string
| boolean
| undefined
| symbol
| null
| bigint
);
Defines a union of all possible strings retuned by applying typeof
operator.
export type TypeName = (
| 'number'
| 'string'
| 'boolean'
| 'undefined'
| 'object'
| 'function'
| 'symbol'
| 'bigint'
);