Non-Distributive Wrapper in TypeScript
This challenge asks you to implement a non-distributive wrapper type in TypeScript. Non-distributive types are useful when you want to create a type that doesn't automatically union types when dealing with intersections. This is in contrast to TypeScript's default distributive behavior, which can sometimes lead to unexpected type narrowing.
Problem Description
You need to create a type called NonDistributiveWrapper<T> that, when applied to a type T, produces a new type that doesn't distribute over intersections. In simpler terms, if T is an intersection of multiple types (e.g., A & B & C), NonDistributiveWrapper<T> should not result in A | B | C. Instead, it should remain as NonDistributiveWrapper<A & B & C>.
Key Requirements:
- The
NonDistributiveWrapper<T>type must preserve the intersection structure of its input type. - It should not perform any type unioning or distributive behavior.
- The wrapper should be generic, accepting any type
T.
Expected Behavior:
Given the following:
type A = { a: string };
type B = { b: number };
type C = { c: boolean };
type Intersected = A & B & C;
type Wrapped = NonDistributiveWrapper<Intersected>;
The type of Wrapped should be NonDistributiveWrapper<A & B & C>, not A | B | C.
Edge Cases to Consider:
- Empty intersections (
A & B & Cwhere at least one ofA,B, orCis never). - Primitive types (e.g.,
string,number,boolean). These should be wrapped without issue. - Union types (e.g.,
A | B). While the primary focus is on intersections, ensure the wrapper doesn't negatively impact union types. It should simply wrap them.
Examples
Example 1:
type A = { a: string };
type B = { b: number };
type Intersected = A & B;
type Wrapped = NonDistributiveWrapper<Intersected>;
// Expected: type Wrapped = NonDistributiveWrapper<A & B>;
// Not: type Wrapped = A | B;
Explanation: The intersection of A and B is preserved within the wrapper.
Example 2:
type StringType = string;
type WrappedString = NonDistributiveWrapper<StringType>;
// Expected: type WrappedString = NonDistributiveWrapper<string>;
Explanation: A primitive type is correctly wrapped.
Example 3:
type UnionType = A | B;
type WrappedUnion = NonDistributiveWrapper<UnionType>;
// Expected: type WrappedUnion = NonDistributiveWrapper<A | B>;
Explanation: The union type is wrapped without distributive behavior.
Constraints
- The solution must be written in TypeScript.
- The
NonDistributiveWrapper<T>type must be a valid TypeScript type definition. - The solution should be relatively concise and readable.
- The solution should not rely on external libraries.
Notes
This problem requires a good understanding of TypeScript's type system, particularly conditional types and mapped types. Consider how you can create a type that effectively "boxes" the input type without allowing TypeScript to perform its usual distributive behavior. The key is to create a type that references the original type, rather than expanding it. Think about using a conditional type to achieve this.