invariant-of
    TypeScript icon, indicating that this package has built-in type declarations

    2.0.0 • Public • Published

    InvariantOf

    Make TypeScript type system invariant

    Motivation

    Type compatibility in TypeScript is based on structural subtyping.

    Therefore, there are some limitations in narrowing the types of Object methods like Object.keys or Object.entries.

    Further reading

    However, if use invariant type system, the following typing is possible.

    export type ObjectKey<O extends object> = Exclude<keyof O, symbol>;
    
    /**
     * Using declaration merging feature
     */
    declare global {
      export interface ObjectConstructor {
        getOwnPropertyNames<T extends object>(o: InvariantOf<T>): Array<ObjectKey<T>>;
        keys<T extends object>(o: InvariantOf<T>): Array<ObjectKey<T>>;
        entries<T extends object>(o: InvariantOf<T>): Array<[ObjectKey<T>, T[ObjectKey<T>]]>;
      }
    }

    It has similar benefit to using a Nominal Type System.

    But, no needs to brand

    How?

    Make object type invariance.

    • Invariance does not accept supertypes.
    • Invariance does not accept subtypes.
    import {invariantOf, InvariantOf} from 'invariant-of';
    
    interface Base {
      foo: string;
    }
    
    interface Derived extends Base {
      bar: string;
    }
    
    declare function method1(value: Base): void;
    declare function method2(value: InvariantOf<Base>): void;
    
    method1({foo: 'foo'} as Base); // Okay
    method1({foo: 'foo', bar: 'bar'} as Derived); // Okay
    
    method2({foo: 'foo'} as InvariantOf<Base>); // Okay
    method2(invariantOf({foo: 'foo'})); // Okay
    method2({foo: 'foo', bar: 'bar'} as InvariantOf<Derived>); // Error

    Here is a comparison with default behavior.

    It does not affect runtime behavior.

    Install

    npm install invariant-of

    Usage

    interface Base {
      foo: number;
      bar?: string;
    }
    
    interface Derived extends Base {
      baz: string;
    }
    
    const someObject: Base = {foo: 123, bar: 'hello'};
    const derivedObject: Derived = {foo: 123, bar: 'hello', baz: 'bye'};
    
    function getKeys(args: InvariantOf<Base>) {
      return Object.keys(args);
    }
    
    getKeys(someObject); // Error
    getKeys(derivedObject); // Error
    getKeys(invariantOf(someObject)); // Work
    getKeys(invariantOf(derivedObject)); // Error

    LICENSE

    MIT

    Install

    npm i invariant-of

    DownloadsWeekly Downloads

    1

    Version

    2.0.0

    License

    MIT

    Unpacked Size

    6.7 kB

    Total Files

    5

    Last publish

    Collaborators

    • younho9