Git Product home page Git Product logo

ts-cookbook's Introduction

TS Cookbook

NPM version Build status Downloads

This library is intended to serve as a cookbook to all manner of type based operations you would need in typescript.

Runtime performance

This library contains some type definitions as well as some helper functions. The type definitions have no runtime performance since they are compiled out. The helper function may incur an infinitesimal amount of runtime performance but will also be compiled out in any production build.

Table of Contents

How do I...

Make all properties on an interface optional

Use the Partial type.

interface Person {
  name: string;
  age: number;
}
type PersonWithAllPropertiesOptional = Partial<Person>;

const person1: PersonWithAllPropertiesOptional = {}; // OK

// OK
const person2: PersonWithAllPropertiesOptional = {
  name: 'Me',
};

// OK
const person3: PersonWithAllPropertiesOptional = {
  name: 'Me',
  age: 123,
};

const person4: PersonWithAllPropertiesOptional = {
  name: 'Me',
  age: 123,
  // Error: Object literal may only specify known properties, and 'foo' does not exist in type 'Partial<Person>'.
  foo: 'bar',
};

Make all properties on an interface required

Use the Required type.

interface Person {
  name: string;
  age?: number;
}

const person: Person = { name: 'Alex' };

// Error: Property 'age' is optional in type 'Person' but required in type 'Required<Person>'.
const requiredPerson: Required<Person> = person;

Make all properties on an interface nullable

Use the Nullable type.

type Nullable<T> = { [P in keyof T]: T[P] | null };

interface Person {
  name: string;
  age: number;
}

// OK
const person: Nullable<Person> = {
  name: null,
  age: null,
};

// OK
person.name = 'Adam';

Make all properties on an interface readonly

Use the Readonly type.

interface Person {
  name: string;
  age?: number;
}

const person: Readonly<Person> = { name: 'Alex' };

// Error: Cannot assign to 'name' because it is a constant or a read-only property.
person.name = 'Bob';

Create a tuple from a known set of elements

Use the tuple function of toopl.

import { tuple } from 'toopl';

// Type: [number, string, boolean]
const myTuple = tuple(1, '2', true);

Create a new type with some keys of another type

Use the Pick type.

interface Person {
  name: string;
  age: number;
  id: number;
}

const personForTest: Pick<Person, 'name'|'age'> = {
  name: 'Charlie',
  age: 123,
};

Remove properties from an interface

Use the OmitStrict type from type-zoo.

import { OmitStrict } from 'type-zoo';

interface Person {
  name: string;
  age: number;
  id: number;
}

const person: OmitStrict<Person, 'age'|'id'> = { name: 'Danny' };

Note: Omit from type-zoo would also work but wouldn't check if the property actually exists on the interface.

Get the Array type, Promise type, Observable type, ...etc from something

Use the infer keyword.

type ArrayUnpacker<T> = T extends Array<infer U> ? U : never;
const stringArray = ['this', 'is', 'cool'];
// Type: string
let unpackedStringArray: ArrayUnpacker<typeof stringArray>;

type PromiseUnpacker<T> = T extends Promise<infer U> ? U : never;
const stringPromise = Promise.resolve('test');
// Type: string
let unpackedStringPromise: PromiseUnpacker<typeof stringPromise>;

class Box<T> {
  constructor(private readonly value: T) {}
}
type BoxUnpacker<T> = T extends Box<infer U> ? U : never;
const myBox = new Box('a string box!');
// Type: string
let myUnpackedBox: BoxUnpacker<typeof myBox>;

Make a readonly object mutable

Use the Mutable type from [ts-cookbook].

import { Mutable } from 'ts-cookbook';

interface ImmutablePerson {
  readonly name: string;
  readonly age: number;
}

const immutablePerson: ImmutablePerson = {
  name: 'Danny',
  age: 50,
};

// Error: Cannot assign to 'age' because it is a read-only property.
immutablePerson.age = 51;

const person: Mutable<ImmutablePerson> = {
  name: 'Eric',
  age: 34,
};

// OK
person.age = 35;

Make some readonly keys on an object be mutable

Use the MutableKeys type from ts-cookbook.

import { MutableKeys } from 'ts-cookbook';

interface ImmutablePerson {
  readonly name: string;
  readonly age: number;
  readonly isPremium: boolean;
}

const immutablePerson: ImmutablePerson = {
  name: 'Danny',
  age: 50,
  isPremium: false,
};

// Error: Cannot assign to 'age' because it is a read-only property.
immutablePerson.age = 51;

const person: MutableKeys<ImmutablePerson, 'age'|'isPremium'> = {
  name: 'Eric',
  age: 34,
  isPremium: false,
};

// OK
person.age = 35;
person.isPremium = true;

// Error: Cannot assign to 'name' because it is a read-only property.
immutablePerson.name = 'Erik';

Create a sub interface explicitly using only some keys of the interface:

Use the Use the Pick type.

interface Person {
  name: string;
  age: number;
  id: string;
}

type PersonWithNameAndAge = Pick<Person, 'name'|'age'>;

const person: PersonWithNameAndAge = { name: 'Greg', age: 23 };

Create a sub interface infering which keys of the interface to use:

Use the inferPick function of ts-cookbook.

import { inferPick } from 'ts-cookbook';

interface Person {
  name: string;
  age: number;
  id: string;
}

const person = inferPick<Person>()({ name: 'Greg', age: 23 });

Create a deep readonly object

Use the Readonly type or readonly function from ts-cookbook. Note: for shallow objects you can use the built in Typescript Readonly type. If you want to ensure that it will work if the object is a Map, Set, or Array, use the ShallowReadonly type (or shallowReadonly function) from ts-cookbook.

import { Readonly, readonly, shallowReadonly } from 'ts-cookbook';

const array = readonly([1, 2, 3]);

// Error: Property 'push' does not exist on type 'ReadonlyArray<number>'.
array.push(4);

class Person {
  constructor(public name: string, public age: number) {}
}

// `person` is Readonly<Person>
const person = readonly(new Person('Harry', 42));

// Error: Cannot assign to 'name' because it is a read-only property
person.name = 'Harr';

const person2: Readonly<Person> = new Person('Kevin', 43);

// Error: Cannot assign to 'name' because it is a read-only property
person.name += '!';

// `map` is a ReadonlyMap<string, string>
const map = readonly(new Map([['foo', 'bar']]));

// Error: Property 'set' does not exist on type 'ReadonlyMap<string, string>'.
map.set('baz', 'bork');

// `myObj` is Readonly<{cool: string}>
const myObj = readonly({ cool: 'thing' });

// Note: `readonly` creates a deep readonly object, as opposed to the native
//   Typescript `Readonly` type which only creates a shallow readonly
//   object. You can still get the inferred readonly behavior in a shallow
//   fashion by using the `shallowReadonly` function from `ts-cookbook`.

const myObj2 = readonly({
  o: {
    prop: 1,
  },
  map: new Map([['foo', 'bar']]),
  a: [1, 2, 3],
});

// Error: Cannot assign to 'prop' because it is a read-only property.
myObj2.o.prop = 2;

// Error: Property 'set' does not exist on type 'DeepReadonlyMap<string, string>'.
myObj2.map.set('boo', 'zaf');

// Error: Property 'push' does not exist on type 'DeepReadonlyArray<number>'.
myObj2.a.push(4);

Remove some types from an interface

Use the RemoveType function from ts-cookbook.

import { RemoveType } from 'ts-cookbook';

interface Person {
  name: string;
  age: number;
  isSaved: boolean;
  save: () => void;
}

const personForTest: RemoveType<Person, boolean|Function> = {
  name: 'Joe',
  age: 44,
};

Create a new interface using some types of another interface

Use the KeepType function from ts-cookbook.

import { KeepType } from 'ts-cookbook';

interface Person {
  name: string;
  age: number;
  isSaved: boolean;
  save: () => void;
}

const personForTest: KeepType<Person, string|number> = {
  name: 'Joe',
  age: 44,
};

Require one and only one property of an object exist

Use the OneOf function from ts-cookbook.

import { OneOf } from 'ts-cookbook';

interface UnsavedRecord {
  name: string;
  age: number;
}

type DbRecord = UnsavedRecord &
  OneOf<{
    draftId: string;
    dbId: string;
  }>;
const record: DbRecord = {} as any;
if (record.dbId) {
  record.draftId; // draftId is typed as `draftId?: undefined`.
}

Advanced Mapped Types Crash Course

The mapped syntax type is somewhat cryptic, here's the general idea of how it breaks down.

First we have a simple key type:

// This is a simple object type
type MyObject = { ['myCoolKey']: string };

let obj: MyObject = { myCoolKey: 'test' };
obj.myCoolKey; // OK

Typescript allows use of a union type (eg 'foo'|'bar') with the in keyword:

type MyObject = { [K in ('foo'|'bar')]: string };

let obj: MyObject = { foo: 'foo', bar: 'BAR' };
obj.foo; // OK
obj.bar; // OK

Another way of getting a union type is by using the keyof keyword:

interface Point { x: number; y: number; }
type PointKeys = keyof Point; // same as `type PointKeys = 'x'|'y'`

Using that knowledge, we can create a mapped type as follows:

interface Person {
  name: string;
  age: number;
}

// Create a readonly person by adding the readonly modifier to the key.
type ReadonlyPerson = { readonly [K in keyof Person]: Person[K] };

The type doesn't need to be tied to an existing type (like Person in the example above) but can be generic as well:

type Readonly<T> = {
  readonly [K in keyof T]: T[K];
};

See the official Typescript handbook for more details.

ts-cookbook's People

Contributors

kolodny avatar

Stargazers

Fredrik Hjelmaeus avatar Opik Sutisna avatar Cuzeac Florin avatar  avatar Ilyes Abdelrazak Beladel avatar  avatar Mohammed Matar avatar Mohanad Fteha avatar Isao Yagi avatar Nikesh Maharjan avatar Dan Nixa avatar Matt Price avatar  avatar conor_mack avatar Dmitry avatar Alex Salas avatar Vic avatar S V avatar Pied Piper avatar Tony Seing avatar Aravind Dammu avatar  avatar Dániel Jankó avatar  avatar Vladimir Demin avatar Andy Merskin avatar Aakash avatar Adriel avatar Javier Maestre avatar Javier Maestre avatar Dmytro Yankovskyi avatar Andrii Oriekhov avatar Hung Viet Dang avatar  avatar Jorge Tovar avatar Ben Lugavere avatar Vlada avatar Marlena Q avatar Alex Astrum avatar Derek Scherger avatar Ahmed Gaber avatar Harry Solovay avatar Tzvi Friedman (pron. Ts-vee) avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.