Miss any of our Open RFC calls?Watch the recordings here! »

type-fest

0.16.0 • Public • Published


type-fest

A collection of essential TypeScript types



Build Status

Many of the types here should have been built-in. You can help by suggesting some of them to the TypeScript project.

Either add this package as a dependency or copy-paste the needed types. No credit required. 👌

PR welcome for additional commonly needed types and docs improvements. Read the contributing guidelines first.

Install

$ npm install type-fest

Requires TypeScript >=3.2

Usage

import {Except} from 'type-fest';
 
type Foo = {
    unicorn: string;
    rainbow: boolean;
};
 
type FooWithoutRainbow = Except<Foo, 'rainbow'>;
//=> {unicorn: string}

API

Click the type names for complete docs.

Basic

Utilities

  • Except - Create a type from an object type without certain keys. This is a stricter version of Omit.
  • Mutable - Convert an object with readonly keys into a mutable object. The inverse of Readonly<T>.
  • Merge - Merge two types into a new type. Keys of the second type overrides keys of the first type.
  • MergeExclusive - Create a type that has mutually exclusive keys.
  • RequireAtLeastOne - Create a type that requires at least one of the given keys.
  • RequireExactlyOne - Create a type that requires exactly a single key of the given keys and disallows more.
  • PartialDeep - Create a deeply optional version of another type. Use Partial<T> if you only need one level deep.
  • ReadonlyDeep - Create a deeply immutable version of an object/Map/Set/Array type. Use Readonly<T> if you only need one level deep.
  • LiteralUnion - Create a union type by combining primitive types and literal types without sacrificing auto-completion in IDEs for the literal type part of the union. Workaround for Microsoft/TypeScript#29729.
  • Promisable - Create a type that represents either the value or the value wrapped in PromiseLike.
  • Opaque - Create an opaque type.
  • SetOptional - Create a type that makes the given keys optional.
  • SetRequired - Create a type that makes the given keys required.
  • ValueOf - Create a union of the given object's values, and optionally specify which keys to get the values from.
  • PromiseValue - Returns the type that is wrapped inside a Promise.
  • AsyncReturnType - Unwrap the return type of a function that returns a Promise.
  • ConditionalKeys - Extract keys from a shape where values extend the given Condition type.
  • ConditionalPick - Like Pick except it selects properties from a shape where the values extend the given Condition type.
  • ConditionalExcept - Like Omit except it removes properties from a shape where the values extend the given Condition type.
  • UnionToIntersection - Convert a union type to an intersection type.
  • Stringified - Create a type with the keys of the given type changed to string type.
  • FixedLengthArray - Create a type that represents an array of the given type and length.

Miscellaneous

Declined types

If we decline a type addition, we will make sure to document the better solution here.

  • Diff and Spread - The PR author didn't provide any real-world use-cases and the PR went stale. If you think this type is useful, provide some real-world use-cases and we might reconsider.
  • Dictionary - You only save a few characters (Dictionary<number> vs Record<string, number>) from Record, which is more flexible and well-known. Also, you shouldn't use an object as a dictionary. We have Map in JavaScript now.
  • SubType - The type is powerful, but lacks good use-cases and is prone to misuse.
  • ExtractProperties and ExtractMethods - The types violate the single responsibility principle. Instead, refine your types into more granular type hierarchies.

Tips

Built-in types

There are many advanced types most users don't know about.

  • Partial<T> - Make all properties in T optional.

    Example

    Playground

    interface NodeConfig {
            appName: string;
            port: number;
    }
     
    class NodeAppBuilder {
            private configuration: NodeConfig = {
                    appName: 'NodeApp',
                    port: 3000
            };
     
            private updateConfig<Key extends keyof NodeConfig>(key: Key, value: NodeConfig[Key]) {
                    this.configuration[key] = value;
            }
     
            config(config: Partial<NodeConfig>) {
                    type NodeConfigKey = keyof NodeConfig;
     
                    for (const key of Object.keys(config) as NodeConfigKey[]) {
                            const updateValue = config[key];
     
                            if (updateValue === undefined) {
                                    continue;
                            }
     
                            this.updateConfig(key, updateValue);
                    }
     
                    return this;
            }
    }
     
    // `Partial<NodeConfig>`` allows us to provide only a part of the
    // NodeConfig interface.
    new NodeAppBuilder().config({appName: 'ToDoApp'});
  • Required<T> - Make all properties in T required.

    Example

    Playground

    interface ContactForm {
            email?: string;
            message?: string;
    }
     
    function submitContactForm(formData: Required<ContactForm>) {
            // Send the form data to the server.
    }
     
    submitContactForm({
            email: 'ex@mple.com',
            message: 'Hi! Could you tell me more about…',
    });
     
    // TypeScript error: missing property 'message'
    submitContactForm({
            email: 'ex@mple.com',
    });
  • Readonly<T> - Make all properties in T readonly.

    Example

    Playground

    enum LogLevel {
            Off,
            Debug,
            Error,
            Fatal
    };
     
    interface LoggerConfig {
            name: string;
            level: LogLevel;
    }
     
    class Logger {
            config: Readonly<LoggerConfig>;
     
            constructor({name, level}: LoggerConfig) {
                    this.config = {name, level};
                    Object.freeze(this.config);
            }
    }
     
    const config: LoggerConfig = {
        name: 'MyApp',
        level: LogLevel.Debug
    };
     
    const logger = new Logger(config);
     
    // TypeScript Error: cannot assign to read-only property.
    logger.config.level = LogLevel.Error;
     
    // We are able to edit config variable as we please.
    config.level = LogLevel.Error;
  • Pick<T, K> - From T, pick a set of properties whose keys are in the union K.

    Example

    Playground

    interface Article {
            title: string;
            thumbnail: string;
            content: string;
    }
     
    // Creates new type out of the `Article` interface composed
    // from the Articles' two properties: `title` and `thumbnail`.
    // `ArticlePreview = {title: string; thumbnail: string}`
    type ArticlePreview = Pick<Article, 'title' | 'thumbnail'>;
     
    // Render a list of articles using only title and description.
    function renderArticlePreviews(previews: ArticlePreview[]): HTMLElement {
            const articles = document.createElement('div');
     
            for (const preview of previews) {
                    // Append preview to the articles.
            }
     
            return articles;
    }
     
    const articles = renderArticlePreviews([
            {
                title: 'TypeScript tutorial!',
                thumbnail: '/assets/ts.jpg'
            }
    ]);
  • Record<K, T> - Construct a type with a set of properties K of type T.

    Example

    Playground

    // Positions of employees in our company.
    type MemberPosition = 'intern' | 'developer' | 'tech-lead';
     
    // Interface describing properties of a single employee.
    interface Employee {
            firstName: string;
            lastName: string;
            yearsOfExperience: number;
    }
     
    // Create an object that has all possible `MemberPosition` values set as keys.
    // Those keys will store a collection of Employees of the same position.
    const team: Record<MemberPosition, Employee[]> = {
            intern: [],
            developer: [],
            'tech-lead': [],
    };
     
    // Our team has decided to help John with his dream of becoming Software Developer.
    team.intern.push({
        firstName: 'John',
        lastName: 'Doe',
        yearsOfExperience: 0
    });
     
    // `Record` forces you to initialize all of the property keys.
    // TypeScript Error: "tech-lead" property is missing
    const teamEmpty: Record<MemberPosition, null> = {
            intern: null,
            developer: null,
    };
  • Exclude<T, U> - Exclude from T those types that are assignable to U.

    Example

    Playground

    interface ServerConfig {
        port: null | string | number;
    }
     
    type RequestHandler = (request: Request, response: Response) => void;
     
    // Exclude `null` type from `null | string | number`.
    // In case the port is equal to `null`, we will use default value.
    function getPortValue(port: Exclude<ServerConfig['port'], null>): number {
        if (typeof port === 'string') {
            return parseInt(port, 10);
        }
     
        return port;
    }
     
    function startServer(handler: RequestHandler, config: ServerConfig): void {
        const server = require('http').createServer(handler);
     
        const port = config.port === null ? 3000 : getPortValue(config.port);
        server.listen(port);
    }
  • Extract<T, U> - Extract from T those types that are assignable to U.

    Example

    Playground

    declare function uniqueId(): number;
     
    const ID = Symbol('ID');
     
    interface Person {
        [ID]: number;
        name: string;
        age: number;
    }
     
    // Allows changing the person data as long as the property key is of string type.
    function changePersonData<
        Obj extends Person,
        Key extends Extract<keyof Person, string>,
        Value extends Obj[Key]
    > (obj: Obj, key: Key, value: Value): void {
        obj[key] = value;
    }
     
    // Tiny Andrew was born.
    const andrew = {
        [ID]: uniqueId(),
        name: 'Andrew',
        age: 0,
    };
     
    // Cool, we're fine with that.
    changePersonData(andrew, 'name', 'Pony');
     
    // Goverment didn't like the fact that you wanted to change your identity.
    changePersonData(andrew, ID, uniqueId());
  • NonNullable<T> - Exclude null and undefined from T.

    Example Works with strictNullChecks set to true. (Read more here)

    Playground

    type PortNumber = string | number | null;
     
    /** Part of a class definition that is used to build a server */
    class ServerBuilder {
            portNumber!: NonNullable<PortNumber>;
     
            port(this: ServerBuilder, port: PortNumber): ServerBuilder {
                    if (port == null) {
                            this.portNumber = 8000;
                    } else {
                            this.portNumber = port;
                    }
     
                    return this;
            }
    }
     
    const serverBuilder = new ServerBuilder();
     
    serverBuilder
            .port('8000')   // portNumber = '8000'
            .port(null)     // portNumber =  8000
            .port(3000);    // portNumber =  3000
     
    // TypeScript error
    serverBuilder.portNumber = null;
  • Parameters<T> - Obtain the parameters of a function type in a tuple.

    Example

    Playground

    function shuffle(input: any[]): void {
        // Mutate array randomly changing its' elements indexes.
    }
     
    function callNTimes<Fn extends (...args: any[]) => any> (func: Fn, callCount: number) {
        // Type that represents the type of the received function parameters.
        type FunctionParameters = Parameters<Fn>;
     
        return function (...args: FunctionParameters) {
            for (let i = 0; i < callCount; i++) {
                func(...args);
            }
        }
    }
     
    const shuffleTwice = callNTimes(shuffle, 2);
  • ConstructorParameters<T> - Obtain the parameters of a constructor function type in a tuple.

    Example

    Playground

    class ArticleModel {
        title: string;
        content?: string;
     
        constructor(title: string) {
            this.title = title;
        }
    }
     
    class InstanceCache<T extends (new (...args: any[]) => any)> {
        private ClassConstructor: T;
        private cache: Map<string, InstanceType<T>> = new Map();
     
        constructor (ctr: T) {
            this.ClassConstructor = ctr;
        }
     
        getInstance (...args: ConstructorParameters<T>): InstanceType<T> {
            const hash = this.calculateArgumentsHash(...args);
     
            const existingInstance = this.cache.get(hash);
            if (existingInstance !== undefined) {
                return existingInstance;
            }
     
            return new this.ClassConstructor(...args);
        }
     
        private calculateArgumentsHash(...args: any[]): string {
            // Calculate hash.
            return 'hash';
        }
    }
     
    const articleCache = new InstanceCache(ArticleModel);
    const amazonArticle = articleCache.getInstance('Amazon forests burining!');
  • ReturnType<T> – Obtain the return type of a function type.

    Example

    Playground

    /** Provides every element of the iterable `iter` into the `callback` function and stores the results in an array. */
    function mapIter<
            Elem,
            Func extends (elemElem) => any,
            Ret extends ReturnType<Func>
    >(iter: Iterable<Elem>, callback: Func): Ret[] {
            const mapped: Ret[] = [];
     
            for (const elem of iter) {
                    mapped.push(callback(elem));
            }
     
            return mapped;
    }
     
    const setObject: Set<string> = new Set();
    const mapObject: Map<number, string> = new Map();
     
    mapIter(setObject, (value: string) => value.indexOf('Foo')); // number[]
     
    mapIter(mapObject, ([key, value]: [number, string]) => {
            return key % 2 === 0 ? value : 'Odd';
    }); // string[]
  • InstanceType<T> – Obtain the instance type of a constructor function type.

    Example

    Playground

    class IdleService {
            doNothing (): void {}
    }
     
    class News {
            title: string;
            content: string;
     
            constructor(title: string, content: string) {
                    this.title = title;
                    this.content = content;
            }
    }
     
    const instanceCounter: Map<Function, number> = new Map();
     
    interface Constructor {
            new(...args: any[]): any;
    }
     
    // Keep track how many instances of `Constr` constructor have been created.
    function getInstance<
            Constr extends Constructor,
            Args extends ConstructorParameters<Constr>
    >(constructor: Constr, ...args: Args): InstanceType<Constr> {
            let count = instanceCounter.get(constructor) || 0;
     
            const instance = new constructor(...args);
     
            instanceCounter.set(constructor, count + 1);
     
            console.log(`Created ${count + 1} instances of ${Constr.name} class`);
     
            return instance;
    }
     
     
    const idleService = getInstance(IdleService);
    // Will log: `Created 1 instances of IdleService class`
    const newsEntry = getInstance(News, 'New ECMAScript proposals!', 'Last month...');
    // Will log: `Created 1 instances of News class`
  • Omit<T, K> – Constructs a type by picking all properties from T and then removing K.

    Example

    Playground

    interface Animal {
            imageUrl: string;
            species: string;
            images: string[];
            paragraphs: string[];
    }
     
    // Creates new type with all properties of the `Animal` interface
    // except 'images' and 'paragraphs' properties. We can use this
    // type to render small hover tooltip for a wiki entry list.
    type AnimalShortInfo = Omit<Animal, 'images' | 'paragraphs'>;
     
    function renderAnimalHoverInfo (animals: AnimalShortInfo[]): HTMLElement {
            const container =  document.createElement('div');
            // Internal implementation.
            return container;
    }

You can find some examples in the TypeScript docs.

Maintainers

License

(MIT OR CC0-1.0)


Get professional support for this package with a Tidelift subscription
Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies.

Install

npm i type-fest

DownloadsWeekly Downloads

35,063,977

Version

0.16.0

License

(MIT OR CC0-1.0)

Unpacked Size

91.5 kB

Total Files

29

Last publish

Collaborators

  • avatar