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:
- Improving type hints in editors by simplifying nested or intersected types.
- 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:
- Type Flattening: Combines properties from intersected types into a single, flat structure.
- Interface to Type Conversion: Transforms interfaces into sealed types, preventing further property additions.
- Improved Assignability: Makes interface-based types assignable to
Record<string, unknown>
and similar structures.
Use Cases:
- Cleaner Type Hints: Simplifies complex type intersections for better readability in IDE tooltips.
- Working with Third-party Types: Allows easier use of interface-based types from external libraries in contexts requiring sealed types.
- 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
* */