Page with useful typescript snippets

Flatten type:

type FlattenType<T> = T extends object
  ? {
      [P in keyof T]: FlattenType<T[P]>;
    }
  : T;

Recursively renaming a property in an object:

type FilterKey<T, U> = {
  [K in keyof T]: K extends keyof U ? never : K;
}[keyof T];
type RenameKeyRecursively<T, U, R> = T extends U
  ? {
      [K in FilterKey<T, U>]: RenameKeyRecursively<T[K], U, R>;
    } & R
  : T;
 
type MyType = {
  quayCraneName: string;
  b?: string;
  c: {
    quayCraneName: string;
    test: number;
  };
};
 
type Filter = {
  quayCraneName: string;
};
 
type ReplacedType = {
  cheName: string;
};
type NewType = FlattenType<RenameKeyRecursively<MyType, Filter, ReplacedType>>;

Remap undefined properties to be allowed optional:

type OptionalProps<T extends object> = Partial<{
  [K in keyof T as T[K] extends NonNullable<T[K]>
    ? never
    : K]: T[K] extends object ? UndefinedToOptional<T[K]> : T[K];
}>;
 
type RequiredProps<T extends object> = {
  [K in keyof T as T[K] extends NonNullable<T[K]>
    ? K
    : never]: T[K] extends object ? UndefinedToOptional<T[K]> : T[K];
};
type UndefinedToOptional<T extends object> = T extends (infer A)[]
  ? A extends object
    ? OptionalProps<A>[] & RequiredProps<A>[]
    : A
  : OptionalProps<T> & RequiredProps<T>;

Specification pattern

Easily composable filters with specifications:

export interface Specification<T> {
  (input: T): boolean;
}
 
export function all<T>(
  ...specifications: Specification<T>[]
): Specification<T> {
  return (input: T): boolean => specifications.every((spec) => spec(input));
}
 
export function some<T>(
  ...specifications: Specification<T>[]
): Specification<T> {
  return (input: T): boolean => specifications.some((spec) => spec(input));
}
 
export function inverse<T>(specification: Specification<T>): Specification<T> {
  return (input) => !specification(input);
}
 
// Usage:
 
function isEqual(num: number): Specification<number> {
  return (numFromList: number) => {
    return num === numFromList;
  };
}
 
function isOdd(): Specification<number> {
  return (numFromList: number) => {
    return numFromList % 2 !== 0;
  };
}
 
console.log([1, 2, 3].filter(some(all(isOdd(), isEqual(1)), isEqual(3)))); // logs 1, 3