Back to Code Bytes
4 min read
Simplify

Description

Simplify Type Utility

Use Simplify<T> when you need to flatten intersected types for clearer type hints.

Simplify<T> is a TypeScript utility type that flattens complex type structures and transforms interfaces into types. It’s particularly useful for:

  1. Improving type hints in editors by simplifying nested or intersected types.
  2. Enhancing type assignability, especially when working with interfaces.

Code Byte

export type Simplify<T> = {[KeyType in keyof T]: T[KeyType]} & {};

This implementation uses mapped types to create a new object type with the same properties as T, and intersects it with an empty object type to flatten the structure.

Key Features:

  1. Type Flattening: Combines properties from intersected types into a single, flat structure.
  2. Interface to Type Conversion: Transforms interfaces into sealed types, preventing further property additions.
  3. Improved Assignability: Makes interface-based types assignable to Record<string, unknown> and similar structures.

Use Cases:

  1. Cleaner Type Hints: Simplifies complex type intersections for better readability in IDE tooltips.
  2. Working with Third-party Types: Allows easier use of interface-based types from external libraries in contexts requiring sealed types.
  3. Type Safety in Functions: Ensures that object types are correctly assignable to function parameters expecting a specific structure.

Comment Snippet

/**
 * Useful to flatten the type output to improve type hints shown in editors. And also to transform an interface into a type to aide with assignability.
 *
 *  @example
 *  import type {Simplify} from 'type-fest';
 *
 *  type PositionProps = {
 * 	top: number;
 * 	left: number;
 * };
 *
 *  type SizeProps = {
 * 	width: number;
 * 	height: number;
 * };
 *
 *  // In your editor, hovering over `Props` will show a flattened object with all the properties.
 *  type Props = Simplify<PositionProps & SizeProps>;
 *
 *  Sometimes it is desired to pass a value as a function argument that has a different type. At first inspection it may seem assignable, and then you discover it is not because the `value`'s type definition was defined as an interface. In the following example, `fn` requires an argument of type `Record<string, unknown>`. If the value is defined as a literal, then it is assignable. And if the `value` is defined as type using the `Simplify` utility the value is assignable.  But if the `value` is defined as an interface, it is not assignable because the interface is not sealed and elsewhere a non-string property could be added to the interface.
 *
 *  If the type definition must be an interface (perhaps it was defined in a third-party npm package), then the `value` can be defined as `const value: Simplify<SomeInterface> = ...`. Then `value` will be assignable to the `fn` argument.  Or the `value` can be cast as `Simplify<SomeInterface>` if you can't re-declare the `value`.
 *
 *  @example
 *  import type {Simplify} from 'type-fest';
 *
 *  interface SomeInterface {
 * 	foo: number;
 * 	bar?: string;
 * 	baz: number | undefined;
 * }
 *
 *  type SomeType = {
 * 	foo: number;
 * 	bar?: string;
 * 	baz: number | undefined;
 * };
 *
 *  const literal = {foo: 123, bar: 'hello', baz: 456};
 *  const someType: SomeType = literal;
 *  const someInterface: SomeInterface = literal;
 *
 *  function fn(object: Record<string, unknown>): void {}
 *
 *  fn(literal); // Good: literal object type is sealed
 *  fn(someType); // Good: type is sealed
 *  fn(someInterface); // Error: Index signature for type 'string' is missing in type 'someInterface'. Because `interface` can be re-opened
 *  fn(someInterface as Simplify<SomeInterface>); // Good: transform an `interface` into a `type`
 *
 *  @link https://github.com/microsoft/TypeScript/issues/15300
 *
 *  @category Object
 * */