Skip to content

Commit

Permalink
Make Simplify handle all types (#414)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
skarab42 and sindresorhus committed Jul 2, 2022
1 parent 67fb0d7 commit 96fe795
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 8 deletions.
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
? 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);

0 comments on commit 96fe795

Please sign in to comment.