Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Simplify handle all types #414

Merged
merged 10 commits into from Jul 2, 2022
2 changes: 1 addition & 1 deletion index.d.ts
Expand Up @@ -38,7 +38,7 @@ export {Entry} from './source/entry';
export {Entries} from './source/entries';
export {SetReturnType} from './source/set-return-type';
export {Asyncify} from './source/asyncify';
export {Simplify} from './source/simplify';
export {Simplify, SimplifyOptions} from './source/simplify';
export {Jsonify} from './source/jsonify';
export {Schema} from './source/schema';
export {LiteralToPrimitive} from './source/literal-to-primitive';
Expand Down
27 changes: 26 additions & 1 deletion source/simplify.d.ts
@@ -1,3 +1,23 @@
/**
@see Simplify
*/
export interface SimplifyOptions {
/**
Do the simplification recursively.

@default false
*/
deep?: boolean;
}

// Flatten a type without worrying about the result.
type Flatten<
AnyType,
Options extends SimplifyOptions = {},
> = Options['deep'] extends true
? {[KeyType in keyof AnyType]: Simplify<AnyType[KeyType], Options>}
: {[KeyType in keyof AnyType]: AnyType[KeyType]};

/**
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.

Expand Down Expand Up @@ -55,4 +75,9 @@ fn(someInterface as Simplify<SomeInterface>); // Good: transform an `interface`

@category Object
*/
export type Simplify<T> = {[KeyType in keyof T]: T[KeyType]};
export type Simplify<
AnyType,
Options extends SimplifyOptions = {},
> = Flatten<AnyType> extends AnyType
skarab42 marked this conversation as resolved.
Show resolved Hide resolved
? Flatten<AnyType, Options>
: AnyType;
54 changes: 48 additions & 6 deletions test-d/simplify.ts
Expand Up @@ -16,15 +16,15 @@ const flattenProps = {top: 120, left: 240, width: 480, height: 600};
expectType<Simplify<PositionProps & SizeProps>>(flattenProps);

interface SomeInterface {
foo: number;
bar?: string;
baz: number | undefined;
foo: number;
bar?: string;
baz: number | undefined;
}

type SomeInterfaceAsTypeWrittenByHand = {
foo: number;
bar?: string;
baz: number | undefined;
foo: number;
bar?: string;
baz: number | undefined;
};

const valueAsLiteral = {foo: 123, bar: 'hello', baz: 456};
Expand All @@ -42,3 +42,45 @@ expectType<SomeInterfaceAsTypeWrittenByHand>(valueAsInterface);
expectAssignable<Record<string, unknown>>(valueAsLiteral);
expectAssignable<Record<string, unknown>>(valueAsSimplifiedInterface);
expectNotAssignable<Record<string, unknown>>(valueAsInterface); // Index signature is missing in interface

// Should return the original type if it is not simplifiable, like a function.
type SomeFunction = (type: string) => string;
expectType<Simplify<SomeFunction>>((type: string) => type);

class SomeClass {
id: string;

private readonly code: number;

constructor() {
this.id = 'some-class';
this.code = 42;
}

someMethod() {
return this.code;
}
}

expectType<Simplify<SomeClass>>(new SomeClass());

// Test deep option
// This is mostly visual, move the mouse over "expectAssignable" to see the result.
type PositionAndSize = PositionProps & SizeProps;

interface Node {
parent: PositionAndSize;
child: {
parent: PositionAndSize;
};
}

const node = {
parent: flattenProps,
child: {
parent: flattenProps,
},
};

expectAssignable<Simplify<Node>>(node);
expectAssignable<Simplify<Node, {deep: true}>>(node);