Hone logo
Hone
Problems

Type Histomorphisms in TypeScript

Histomorphisms, in the context of type theory, are mappings between types that preserve structure. This challenge asks you to implement a function that, given two TypeScript types, attempts to create a "histomorphic" mapping – essentially, a transformation that maps fields from one type to another while maintaining as much structural similarity as possible. This is useful for scenarios like data migration, type adaptation, and creating generic components that can work with different data structures.

Problem Description

You are tasked with creating a TypeScript function histomorphize that takes two TypeScript types, sourceType and targetType, and returns a new type that represents a histomorphic mapping from sourceType to targetType. The function should attempt to map properties from sourceType to targetType based on their names. If a property exists in sourceType but not in targetType, it should be omitted from the resulting type. If a property exists in both types, it should be mapped directly.

The function should handle the following:

  • Primitive Types: string, number, boolean, null, undefined, symbol should be mapped directly if the property exists in both types.
  • Object Types: If both types have a property of an object type, the resulting type should be an object type with the mapped properties.
  • Union Types: If a property in sourceType is a union type, the resulting type should also be a union type, mapping each member of the union.
  • Optional Properties: Optional properties (?) should be preserved in the resulting type.
  • Readonly Properties: Readonly properties (:) should be preserved in the resulting type.
  • Intersection Types: Intersection types should be handled by mapping all properties from both intersected types.
  • Type Aliases: Type aliases should be resolved and mapped correctly.

Expected Behavior:

The histomorphize function should return a TypeScript type that represents the histomorphic mapping. The returned type should be structurally equivalent to the mapping described above.

Edge Cases to Consider:

  • Circular references between types (the function should not enter an infinite loop). A simple check for already visited types is sufficient.
  • Complex types like conditional types and mapped types (these are beyond the scope of this challenge and can be ignored).
  • Types with the same name but different structures.
  • Nested object types.

Examples

Example 1:

Input:
sourceType: { a: string; b: number; c?: boolean }
targetType: { a: string; b: number; d: string }

Output:
{ a: string; b: number }

Explanation:
The function maps 'a' and 'b' because they exist in both types. 'c' is omitted because it's not in `targetType`. 'd' is not included because it's only in `targetType`.

Example 2:

Input:
sourceType: { name: string; age: number; address: { street: string; city: string } }
targetType: { name: string; address: { city: string; zip: number } }

Output:
{ name: string; address: { city: string } }

Explanation:
'name' is mapped. 'address' is mapped, but only the 'city' property is included because it exists in both the source and target address types.

Example 3:

Input:
sourceType: { a: string | number; b: boolean }
targetType: { a: string; b: boolean }

Output:
{ a: string | number; b: boolean }

Explanation:
The union type 'a: string | number' is preserved because it exists in both types. 'b' is also preserved.

Constraints

  • The function must be written in TypeScript.
  • The function should handle types with a maximum depth of 5 nested objects. This prevents infinite recursion in complex type structures.
  • The function should not throw errors if it encounters a type it cannot fully map. It should gracefully omit unmappable properties.
  • Performance is not a primary concern, but avoid excessively inefficient algorithms.

Notes

  • You can use TypeScript's type manipulation capabilities (e.g., typeof, keyof, Pick, Omit, Partial, Required) to achieve the desired mapping.
  • Consider using a recursive approach to handle nested object types.
  • The goal is to create a reasonable histomorphic mapping, not a perfect one. Focus on preserving the core structure and mapping existing properties.
  • This challenge is designed to be complex and requires a good understanding of TypeScript's type system. Don't be afraid to experiment and iterate.
  • You do not need to handle conditional or mapped types. Focus on the core mapping of object properties.
Loading editor...
typescript