From 96fe79598ec20fb9adad26cad95a9daa46718b0d Mon Sep 17 00:00:00 2001 From: skarab42 Date: Sat, 2 Jul 2022 13:49:45 +0200 Subject: [PATCH] Make `Simplify` handle all types (#414) Co-authored-by: Sindre Sorhus --- index.d.ts | 2 +- source/simplify.d.ts | 27 +++++++++++++++++++++- test-d/simplify.ts | 54 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 75 insertions(+), 8 deletions(-) diff --git a/index.d.ts b/index.d.ts index bcce1e75d..9f23234f6 100644 --- a/index.d.ts +++ b/index.d.ts @@ -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'; diff --git a/source/simplify.d.ts b/source/simplify.d.ts index 275ec6a92..7112dc3f2 100644 --- a/source/simplify.d.ts +++ b/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} + : {[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. @@ -55,4 +75,9 @@ fn(someInterface as Simplify); // Good: transform an `interface` @category Object */ -export type Simplify = {[KeyType in keyof T]: T[KeyType]}; +export type Simplify< + AnyType, + Options extends SimplifyOptions = {}, +> = Flatten extends AnyType + ? Flatten + : AnyType; diff --git a/test-d/simplify.ts b/test-d/simplify.ts index e9192b174..d08fef620 100644 --- a/test-d/simplify.ts +++ b/test-d/simplify.ts @@ -16,15 +16,15 @@ const flattenProps = {top: 120, left: 240, width: 480, height: 600}; expectType>(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}; @@ -42,3 +42,45 @@ expectType(valueAsInterface); expectAssignable>(valueAsLiteral); expectAssignable>(valueAsSimplifiedInterface); expectNotAssignable>(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>((type: string) => type); + +class SomeClass { + id: string; + + private readonly code: number; + + constructor() { + this.id = 'some-class'; + this.code = 42; + } + + someMethod() { + return this.code; + } +} + +expectType>(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>(node); +expectAssignable>(node);