DevilZh / ts-essentials

All basic TypeScript types in one place πŸ€™

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ts-essentials

ts-essentials

All essential TypeScript types in one place πŸ€™

Downloads Build status Software License All Contributors codechecks.io

Install

npm install --save-dev ts-essentials

πŸ‘‰ We require typescript>=3.7. If you're looking for support for older TS versions use ts-essentials@3 (for 3.6>=) or ts-essentials@2 instead.

What's inside?

Basic

  • Primitive type matching all primitive values.

Dictionaries

keywords: map

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

Deep Partial & Deep Required & Deep Readonly & Deep NonNullable

keywords: nested, optional

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'
  }
}

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,
    },
  },
];

// we can freely write to this object
test[0].foo = "b";
test[0].bar.x = 2;

Buildable

keywords: builder

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">;

// Result:
// {
//  simple: number
// }

// if you want to Omit multiple properties just use union type:
type SimplifiedComplexObject = StrictOmit<ComplexObject, "nested" | "simple">;

// Result:
// { } (empty type)

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">;

// 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.

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,
  }
}>

// The result will be:
// {
//  name: string,
//  students: {name: string}[]
// }

NOTE

  • DeepOmit works fine with Arrays and Sets. 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

keywords: filter, props

Removes all properties extending type P in type T. NOTE: it works opposite to filtering.

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)

PickProperties

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;
// }

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.

/* 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<{}>;

Merge

keywords: override

type Foo = {
  a: number;
  b: string;
};

type Bar = {
  b: number;
};

const xyz: Merge<Foo, Bar> = { 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.

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>;
}

MarkOptional

Useful when you want to make some properties optional without creating a separate type.

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
}

type UserWithoutPassword = MarkOptional<User, 'password'>;

// Result:

// {
//   id: number;
//   name: string;
//   email: string;
//   password?: string;
// }

ReadonlyKeys

Gets keys of an object which are readonly.

type T = {
  readonly a: number;
  b: string;
};
type Result = ReadonlyKeys<T>
// Result:
// "a"

WritableKeys

Gets keys of an object which are writable.

type T = {
  readonly a: number;
  b: string;
};
type Result = WritableKeys<T>
// Result:
// "b"

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; // this ugly cast is required but only when "producing" opaque types
}

Tuple constraint

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>.

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:
      // if you would add another option to DummyOptions, you'll get error here!
      throw new UnreachableCaseError(options);
  }
}

ValueOf type

const obj = {
  id: "123e4567-e89b-12d3-a456-426655440000",
  name: "Test object",
  timestamp: 1548768231486,
};

type objKeys = ValueOf<typeof obj>;
// Result: string | number

AsyncOrSync type

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";
  }
}

Assertions

keywords: invariant

Simple runtime assertion that narrows involved types using assertion functions.

Note: This function is not purely type level and leaves minimal runtime trace in generated code.

const something: string | undefined = "abc" as any
assert(something, "Something has to be defined!")
// from now on `something` is string, if this wouldn't be a case, assert would throw

const anything = "abc" as any
assert(anything instanceof String, "anything has to be a string!")
// from now on `anything` is string

XOR

Gets the XOR (Exclusive-OR) type which could make 2 types exclude each other.

  type A = {a: string}
  type B = {a: number; b: boolean}
  type C = {c: number}

  let A_XOR_B: XOR<A, B>
  let A_XOR_C: XOR<A, C>

  // fail
  A_XOR_B = {a: 0}
  A_XOR_B = {b: true}
  A_XOR_B = {a: '', b: true}
  A_XOR_C = {a: '', c: 0} // would be allowed with `A | C` type

  // ok
  A_XOR_B = {a: 0, b: true}
  A_XOR_B = {a: ''}
  A_XOR_C = {c: 0}

Contributors

Thanks goes to these wonderful people (emoji key):

Chris Kaczor
Chris Kaczor

πŸ’» πŸ’Ό πŸ’‘ πŸ“–
Xiao Liang
Xiao Liang

πŸ’» πŸ€” πŸ“–
Mateusz BurzyΕ„ski
Mateusz BurzyΕ„ski

πŸ’» πŸ€” πŸ“–
Maciej Bembenista
Maciej Bembenista

πŸ’» πŸ€” πŸ“–
Michael Tontchev
Michael Tontchev

πŸ’» πŸ€” πŸ“–
Thomas den Hollander
Thomas den Hollander

πŸ’» πŸ€” πŸ“–
Esa-Matti Suuronen
Esa-Matti Suuronen

πŸ’» πŸ€” πŸ“–
Ilya Semenov
Ilya Semenov

πŸ’» πŸ€” πŸ“–
Code Checks
Code Checks

πŸ‘€
Patricio Palladino
Patricio Palladino

πŸ€”
Artur Kozak
Artur Kozak

πŸ’» πŸ€” πŸ“– πŸ‘€
Zihua Wu
Zihua Wu

πŸ’» πŸ€” πŸ“–
Kevin Peno
Kevin Peno

πŸ’»
Dom Parfitt
Dom Parfitt

πŸ€”
EduardoRFS
EduardoRFS

πŸ’» πŸ“–
Andrew C. Dvorak
Andrew C. Dvorak

πŸ“–
Adam Russell
Adam Russell

πŸ’» πŸ“–

This project follows the all-contributors specification. Contributions of any kind welcome! Read more

About

All basic TypeScript types in one place πŸ€™

License:MIT License


Languages

Language:TypeScript 100.0%