Skip to content

A collection of useful operators to make type-level programming in TypeScript easier

License

Notifications You must be signed in to change notification settings

0x414c/type-ops

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

91 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

npm version

type-ops

Type-ops -- a collection of useful operators to make type-level programming in TypeScript easier.

Table of contents

Compatibility

TypeScript ~2.9.0, ^3.0.1.

Install

npm i type-ops

Requirements

For some operators to work properly, strict mode must be enabled.

Dependencies

None.

License

MIT.

TODO

  • Use dtslint.

Features

Testing & checking utilities

These types can be used to test an arbitrary type for being conformant to a certain constraint.

expect

Induce a compilation error if TActual does not resolve to the type of expected.

Definition
declare const expect: <TActual>() => { toBe(expected: TActual): void; };
Usage
interface I1 {
  p1: string;
}
interface I2 {
  p1: number;
}
// There will be a compilation error if `IsSameT<I1, I2>' does not resolve to `false':
expect<IsSameT<I1, I2>>().toBe(false);

ExpectT

Induce a compilation error if TActual does not resolve to TExpected.

Definition
type ExpectT<TActual extends TExpected, TExpected extends boolean> = never;
Usage
interface I1 { p1: string; }
interface I2 { p1: number; }
// Compilation will fail if `IsSameT<I1, I2>' does not resolve to `false':
type E1 = ExpectT<IsSameT<I1, I2>, false>;

IsFalseT

Check if T is false. Does not distribute over unions.

Definition
type IsFalseT<T extends boolean> = IsSameT<T, false>;

IsNeverT

Check if T is never. Does not distribute over unions.

Definition
type IsNeverT<T> = IsSameT<T, never>;

IsNullableT

Check if T is nullable. Does not distribute over unions.

Definition
type IsNullableT<T> = IsSubtypeOfT<undefined | null, T>;

IsOptionalT

Check if T is optional. Does not distribute over unions.

Definition
type IsOptionalT<T> = IsSubtypeOfT<undefined, T>;

IsSameT

Check if T and U are of the same shape. Does not distribute over unions.

Definition
type IsSameT<T, U> = AndT<IsSubtypeOfT<T, U>, IsSubtypeOfT<U, T>>;

IsSubtypeOfT

Check if T is a subtype of U. Does not distribute over unions.

type IsSubtypeOfT<T, U> = NoDistributeT<T> extends U
  ? true
  : false;

IsTrueT

Check if T is true. Does not distribute over unions.

Definition
type IsTrueT<T extends boolean> = NotT<IsFalseT<T>>;

Property selectors

These types can be thought of as "filters" on property keys of a particular type.

KeyofT

Extract all properties of T. Distributes over unions.

Definition
type KeyofT<T> = T extends any
  ? keyof T
  : never;

NotPropertiesOfSubtypeT

Extract all properties of T which are not a subtype of U.

Definition
type NotPropertiesOfSubtypeT<T, U> = Exclude<keyof T, PropertiesOfSubtypeT<T, U>>;

NotPropertiesOfTypeT

Extract all properties of T which are not of the same shape as U.

Definition
type NotPropertiesOfTypeT<T, U> = Exclude<keyof T, PropertiesOfTypeT<T, U>>;

OptionalPropertiesT

Extract all optional properties of T.

Definition
type OptionalPropertiesT<T> = {
  [K in keyof T]-?: IsOptionalT<T[K]> extends false
    ? never
    : K;
}[keyof T];

PropertiesOfSubtypeT

Extract all properties of T which are a subtype of U.

Definition
type PropertiesOfSubtypeT<T, U> = {
  [K in keyof T]: IsSubtypeOfT<T[K], U> extends false
    ? never
    : K;
}[keyof T];

PropertiesOfTypeT

Extract all properties of T which are of the same shape as U.

Definition
type PropertiesOfTypeT<T, U> = {
  [K in keyof T]: IsSameT<T[K], U> extends false
    ? never
    : K;
}[keyof T];

RequiredPropertiesT

Extract all required properties of T.

Definition
type RequiredPropertiesT<T> = {
  [K in keyof T]-?: IsOptionalT<T[K]> extends false
    ? K
    : never;
}[keyof T];

Type modifiers

These are just mapped conditional types.

JsonT

Represent T after JSON serialization round-trip. Distributes over unions.

Definition
interface _JsonArrayT<T> extends Array<JsonT<T>> { }
type _CleanT<T> = OmitT<T, PropertiesOfTypeT<T, never>>;
type _JsonObjectT<T> = {
  [K in keyof T]: JsonT<T[K]>;
};
type JsonT<T> = T extends string | number | boolean | null
  ? T
  : T extends Function | symbol | undefined
    ? never
    : T extends Array<infer U> | ReadonlyArray<infer U>
      ? _JsonArrayT<U>
      : T extends Map<any, any> | ReadonlyMap<any, any> | WeakMap<any, any> | Set<any> | ReadonlySet<any> | WeakSet<any>
        ? { }
        : T extends { toJSON(key?: any): infer U; }
          ? U
          : _CleanT<_JsonObjectT<T>>;

OmitT

Omit properties K from T.

Definition
type OmitT<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

OverwriteT

Overwrite properties K of T with matching properties of U and add properties which are unique to U.

Definition
type OverwriteT<T, U, K extends keyof T & keyof U = keyof T & keyof U> = OmitT<T, K> & U;
Usage
interface I1 {
  p1: string;
  p2: string;
  p3: string;
}
interface I2 {
  p2: number;
  p3: number;
  p4: number;
}
type I3 = OverrideT<I1, I2>;
const i3: I3 = {
  p1: 'v1',
  p2: 2,
  p3: 3,
  p4: 4,
};

PartialDeepT

Recursively make all properties of T optional.

Definition
interface _PartialDeepArray<T> extends Array<PartialDeepT<T>> { }
interface _PartialDeepReadonlyArray<T> extends ReadonlyArray<PartialDeepT<T>> { }
type _PartialDeepObjectT<T> = {
  [K in keyof T]: PartialDeepT<T[K]>;
};
export type PartialDeepT<T> = T extends Array<infer U>
  ? _PartialDeepArray<U>
  : T extends ReadonlyArray<infer U>
    ? _PartialDeepReadonlyArray<U>
    : T extends Function | PrimitiveT
      ? T
      : Partial<_PartialDeepObjectT<T>>;

ReadonlyDeepT

Recursively make all properties of T readonly.

Definition
interface _ReadonlyDeepArray<T> extends Array<ReadonlyDeepT<T>> { }
interface _ReadonlyDeepReadonlyArray<T> extends ReadonlyArray<ReadonlyDeepT<T>> { }
type _ReadonlyDeepObjectT<T> = {
  [K in keyof T]: ReadonlyDeepT<T[K]>;
};
export type ReadonlyDeepT<T> = T extends Array<infer U>
  ? _ReadonlyDeepArray<U>
  : T extends ReadonlyArray<infer U>
    ? _ReadonlyDeepReadonlyArray<U>
    : T extends Function | PrimitiveT
      ? T
      : Readonly<_ReadonlyDeepObjectT<T>>;

ReplaceT

Replace properties K of T with matching properties of U.

Definition
type ReplaceT<T, U, K extends keyof T & keyof U = keyof T & keyof U> = OmitT<T, K> & Pick<U, K>;
Usage
interface I1 {
  p1: string;
  p2: string;
  p3: string;
  p4: string;
}
interface I2 {
  p2: number;
  p3: number;
  p4: number;
  p5: number;
}
type I3 = ReplaceT<I1, I2, 'p2' | 'p4'>;
const i3: I3 = {
  p1: 'v1',
  p2: 2,
  p3: 'v3',
  p4: 4,
};

RequiredDeepT

Recursively make all properties of T required.

Definition
interface _RequiredDeepArray<T> extends Array<RequiredDeepT<T>> { }
interface _RequiredDeepReadonlyArray<T> extends ReadonlyArray<RequiredDeepT<T>> { }
type _RequiredDeepObjectT<T> = {
  [K in keyof T]: RequiredDeepT<T[K]>;
};
export type RequiredDeepT<T> = T extends Array<infer U>
  ? _RequiredDeepArray<U>
  : T extends ReadonlyArray<infer U>
    ? _RequiredDeepReadonlyArray<U>
    : T extends Function | PrimitiveT
      ? T
      : _RequiredDeepObjectT<Required<T>>;

WithMutableT

Make properties K of T mutable.

Definition
type WithMutableT<T, K extends keyof T> = ReplaceT<T, MutableT<T>, K>;

WithPartialT

Make properties K of T optional.

Definition
type WithPartialT<T, K extends keyof T> = ReplaceT<T, Partial<T>, K>;

WithReadonlyT

Make properties K of T readonly.

Definition
type WithReadonlyT<T, K extends keyof T> = ReplaceT<T, Readonly<T>, K>;

WithRequiredT

Make properties K of T required.

Definition
type WithRequiredT<T, K extends keyof T> = ReplaceT<T, Required<T>, K>;

WritableDeepT

Recursively make all properties of T mutable.

Definition
interface _MutableDeepArray<T> extends Array<MutableDeepT<T>> { }
interface _MutableDeepReadonlyArray<T> extends ReadonlyArray<MutableDeepT<T>> { }
type _MutableDeepObjectT<T> = {
  [K in keyof T]: MutableDeepT<T[K]>;
};
type MutableDeepT<T> = T extends Array<infer U>
  ? _MutableDeepArray<U>
  : T extends ReadonlyArray<infer U>
    ? _MutableDeepReadonlyArray<U>
    : T extends Function | PrimitiveT
      ? T
      : _MutableDeepObjectT<MutableT<T>>;

MutableT

Make all properties of T mutable.

Definition
type MutableT<T> = {
  -readonly [K in keyof T]: T[K];
};

Aliases and interfaces

ConstructorT

A constructor of TInstances from TArguments.

Definition
type ConstructorT<TInstance = object, TArguments extends any[] = any[]> = new(...args: TArguments) => TInstance;

DictT

A dictionary of TValues.

Definition
interface DictT<TValue = any> {
  [propertyKey: string]: TValue;
}

FunctionT

A function mapping TArguments to TResult.

Definition
type FunctionT<TResult = any, TArguments extends any[] = any[]> = (...args: TArguments) => TResult;

NullableT

Make T nullable.

Definition
type NullableT<T> = OptionalT<T> | null;

OptionalT

Make T optional.

Definition
type OptionalT<T> = T | undefined;

PrimitiveT

A primitive.

Definition
type PrimitiveT = string | symbol | number | boolean | undefined | null;

TaggedT

Make tagged type from T using tag TTag. It can be used to create an opaque alias of T.

Definition
declare const _RAW_TYPE: unique symbol;
declare const _TAG_TYPE: unique symbol;
type TaggedT<T, TTag extends PropertyKey> = T & {
  [_RAW_TYPE]: T;
  [_TAG_TYPE]: TTag;
};
Usage
// `I1' is an opaque type alias of `string' tagged w/ `A':
type I1 = UniqueT<string, 'A'>;
// Type assertion must be used to assign raw `string' to its opaque typedef:
let i1: I1 = 'v1' as I1;
i1 = 'v2' as I1;
// i1 = 'v3';  // Compilation will fail.
// Underlying raw type (`string') can be retrieved through lookup type:
const i11: RawT<I1> = i1;

// `I2' has the same shape as `I1':
type I2 = UniqueT<string, 'A'>;
let i2: I2 = 'v4' as I2;
i2 = i1;
i1 = i2;
const i21: RawT<I1> = i1;

// `I3' is an opaque type alias of `string' tagged w/ `B'.
type I3 = UniqueT<string, 'B'>;
let i3: I3 = 'v5' as I3;
// `I1' and `I3' are incompatible:
// i1 = i3;
// i1 = i3 as I1;  // Type assertion will not make any difference.
// i3 = i1;
// i3 = i1 as I3;  // Ditto.
const i31: RawT<I1> = i1;

// `I4' has the same shape as `I1':
type I4 = RetaggedT<U3, 'A'>;
let i4: I4 = i3 as RawT<I3> as I4;
i4 = i1;
i1 = i4;
RawT

Extract raw type of tagged T. Distributes over unions.

Definition
type RawT<T> = T extends UniqueT<infer U, infer TTag_>
  ? U
  : T;
TagT

Extract tag from tagged T. Distributes over unions.

Definition
type TagT<T> = T extends UniqueT<infer U_, infer TTag>
  ? TTag
  : never;
RetaggedT

Retag a tagged T using tag TTag. Distributes over unions.

Definition
type RetaggedT<T, TTag extends PropertyKey> = T extends TaggedT<infer U, infer TTag_>
  ? TaggedT<U, TTag>
  : TaggedT<T, TTag>;

Logical operators

AndT

Logical "and" of T and U. Distributes over unions.

Definition
type AndT<T extends boolean, U extends boolean> = T extends false
  ? false
  : U extends false
    ? false
    : true;

NotT

Logical "not" of T. Distributes over unions.

Definition
type NotT<T extends boolean> = T extends false
  ? true
  : false;

OrT

Logical "or" of T and U. Distributes over unions.

Definition
type OrT<T extends boolean, U extends boolean> = T extends false
  ? U extends false
    ? false
    : true
  : true;

XorT

Logical "xor" of T and U. Distributes over unions.

Definition
type XorT<T extends boolean, U extends boolean> = T extends false
  ? U extends false
    ? false
    : true
  : U extends false
    ? true
    : false;

Miscellaneous utilities

NoDistributeT

Prevent distribution over T.

Definition
type NoDistributeT<T> = T extends any
  ? T
  : never;

NoInferT

Prevent type inference on T.

Definition
type NoInferT<T> = T & Pick<T, keyof T>;
Usage
declare const f1: <T>(i1: T, i2: T) => void;
f1({ p1: 'v1', p2: 'v2' }, { p1: 'v1' });  // An error sneaks in.

declare const f2: <T>(i1: T, i2: NoInferT<T>) => void;
// f2({ p1: 'v1', p2: 'v2' }, { p1: 'v1' });  // Causes compilation error.
f2({ p1: 'v1', p2: 'v2' }, { p1: 'v1', p2: 'v2' });

SelectT

Extract a member of a tagged union T using TTagKey as a tag property and TValue as its type.

Definition
type SelectT<T, TTagKey extends keyof T, TTagValue extends T[TTagKey]> = Extract<T, Record<TTagKey, TTagValue>>;
Usage
declare const TAG: unique symbol;
interface I1 {
  [TAG]: 'I1';
  p1: string;
}
interface I2 {
  [TAG]: 'I2';
  p1: number;
}
type I3 = I1 | I2;
type A11 = TaggedUnionMemberT<I3, typeof TAG, 'I1'>;  // Resolves to `I1'.

About

A collection of useful operators to make type-level programming in TypeScript easier

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published