Skip to content

Issafeature/typescript-tricks

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 

Repository files navigation

TypeScript Tricks

My collection of TypeScript tricks from various sources.

For Utility Types, you may have better luck with:

DeepImmutable aka DeepReadonly Generic

Deep immutable (readonly) generic type for specifying multi-level data structures that cannot be modified.

Example:

let deepX: DeepImmutable<{y: {a: number}}> = {y: {a: 1}};
deepX.y.a = 2; // Fails as expected!

Credit: @nieltg in Microsoft/TypeScript#13923 (comment)

type Primitive = undefined | null | boolean | string | number | Function

type Immutable<T> =
  T extends Primitive ? T :
    T extends Array<infer U> ? ReadonlyArray<U> :
      T extends Map<infer K, infer V> ? ReadonlyMap<K, V> : Readonly<T>

type DeepImmutable<T> =
  T extends Primitive ? T :
    T extends Array<infer U> ? DeepImmutableArray<U> :
      T extends Map<infer K, infer V> ? DeepImmutableMap<K, V> : DeepImmutableObject<T>

interface DeepImmutableArray<T> extends ReadonlyArray<DeepImmutable<T>> {}
interface DeepImmutableMap<K, V> extends ReadonlyMap<DeepImmutable<K>, DeepImmutable<V>> {}
type DeepImmutableObject<T> = {
  readonly [K in keyof T]: DeepImmutable<T[K]>
}

Opaque Generic

A generic type that allows for checking based on the name of the type ("opaque" type checking) as opposed to the data type ("transparent", the default in TypeScript).

Example:

type Username = Opaque<"Username", string>;
type Password = Opaque<"Password", string>;

function createUser(username: Username, password: Password) {}
const getUsername = () => getFormInput('username') as Username;
const getPassword = () => getFormInput('password') as Password;

createUser(
  getUsername(),
  getUsername(),  // Error: Argument of type 'Opaque<"Username", string>' is not assignable to
                  // parameter of type 'Opaque<"Password", string>'.
);

Credit:

type Opaque<K, T> = T & { __TYPE__: K };

Spread Generic

A generic type that allows for more soundness while using object spreads and Object.assign.

type A = {
    a: boolean;
    b: number;
    c: string;
};

type B = {
    b: number[];
    c: string[] | undefined;
    d: string;
    e: number | undefined;
};

type AB = Spread<A, B>;

// type AB = {
//    a: boolean;
//    b: number[];
//    c: string | string[];
//    d: string;
//    e: number | undefined;
//};

Credit:

type Diff<T, U> = T extends U ? never : T;  // Remove types from T that are assignable to U

// Names of properties in T with types that include undefined
type OptionalPropertyNames<T> =
    { [K in keyof T]: undefined extends T[K] ? K : never }[keyof T];

// Common properties from L and R with undefined in R[K] replaced by type in L[K]
type SpreadProperties<L, R, K extends keyof L & keyof R> =
    { [P in K]: L[P] | Diff<R[P], undefined> };

// Type of { ...L, ...R }
type Spread<L, R> =
    // Properties in L that don't exist in R
    & Pick<L, Diff<keyof L, keyof R>>
    // Properties in R with types that exclude undefined
    & Pick<R, Diff<keyof R, OptionalPropertyNames<R>>>
    // Properties in R, with types that include undefined, that don't exist in L
    & Pick<R, Diff<OptionalPropertyNames<R>, keyof L>>
    // Properties in R, with types that include undefined, that exist in L
    & SpreadProperties<L, R, OptionalPropertyNames<R> & keyof L>;

About

A collection of useful TypeScript tricks

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published