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

Dependents (0)

Package Sidebar

Install

npm i invariant-of

Weekly Downloads

0

Version

2.0.0

License

MIT

Unpacked Size

6.7 kB

Total Files

5

Last publish

Collaborators

  • younho9