From beaabe1821e58dfc8bf83d5bd547de69c7c95763 Mon Sep 17 00:00:00 2001 From: skarab42 Date: Wed, 24 Aug 2022 22:28:10 +0200 Subject: [PATCH] Add `ConditionalSimplify` and `ConditionalSimplifyDeep` types (#442) --- source/conditional-simplify.d.ts | 32 +++++++++++++ test-d/conditional-simplify.ts | 81 ++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 source/conditional-simplify.d.ts create mode 100644 test-d/conditional-simplify.ts diff --git a/source/conditional-simplify.d.ts b/source/conditional-simplify.d.ts new file mode 100644 index 00000000..a752a3f2 --- /dev/null +++ b/source/conditional-simplify.d.ts @@ -0,0 +1,32 @@ +/** +Simplifies a type while including and/or excluding certain types from being simplified. Useful to improve type hints shown in editors. And also to transform an interface into a type to aide with assignability. + +This type is **experimental** and was introduced as a result of this {@link https://github.com/sindresorhus/type-fest/issues/436 issue}. It should be used with caution. + +@internal +@experimental +@see Simplify +@category Object +*/ +export type ConditionalSimplify = Type extends ExcludeType + ? Type + : Type extends IncludeType + ? {[TypeKey in keyof Type]: Type[TypeKey]} + : Type; + +/** +Recursively simplifies a type while including and/or excluding certain types from being simplified. + +This type is **experimental** and was introduced as a result of this {@link https://github.com/sindresorhus/type-fest/issues/436 issue}. It should be used with caution. + +See {@link ConditionalSimplify} for usages and examples. + +@internal +@experimental +@category Object +*/ +export type ConditionalSimplifyDeep = Type extends ExcludeType + ? Type + : Type extends IncludeType + ? {[TypeKey in keyof Type]: ConditionalSimplifyDeep} + : Type; diff --git a/test-d/conditional-simplify.ts b/test-d/conditional-simplify.ts new file mode 100644 index 00000000..9838bc23 --- /dev/null +++ b/test-d/conditional-simplify.ts @@ -0,0 +1,81 @@ +import {expectError, expectType} from 'tsd'; +import type {ConditionalSimplify, ConditionalSimplifyDeep} from '../source/conditional-simplify'; + +type Position = {top: number; left: number}; +type Size = {width: number; height: number}; + +// In your editor, hovering over `PositionAndSizeSimplified` will show a simplified object with all the properties. +type PositionAndSizeIntersection = Position & Size; +type PositionAndSizeSimplified = ConditionalSimplify; + +const position = {top: 120, left: 240}; +const size = {width: 480, height: 600}; +const positionAndSize = {...position, ...size}; +expectType(positionAndSize); + +// Exclude function type to be simplified. +type SomeFunction = (type: string) => string; +type SimplifiedFunctionFail = ConditionalSimplify; // Return '{}' +type SimplifiedFunctionPass = ConditionalSimplify; // Return '(type: string) => string' + +declare const simplifiedFunctionFail: SimplifiedFunctionFail; +declare const simplifiedFunctionPass: SimplifiedFunctionPass; + +expectError(simplifiedFunctionFail); +expectType(simplifiedFunctionPass); + +// Should simplify interface deeply. +interface SomeNode { + parent: PositionAndSizeIntersection; + childs: Array<{parent: PositionAndSizeIntersection}>; +} + +// In your editor, hovering over `SomeNodeSimplified` will show a simplified object with all the properties. +type SomeNodeSimplified = ConditionalSimplifyDeep; + +const someNode = {parent: positionAndSize, childs: [{parent: positionAndSize}, {parent: positionAndSize}]}; +expectType(someNode); + +// Should simplify interface deeply excluding Function type. +interface MovablePosition extends Position { + move(position: Position): Position; +} + +interface MovableCollection { + position: MovablePosition; + top: {position: MovablePosition; size: Size}; + left: {position: MovablePosition; size: Size}; +} + +type MovableNodeSimplifiedFail = ConditionalSimplifyDeep; +type MovableNodeSimplifiedPass = ConditionalSimplifyDeep; + +declare const movableNodeSimplifiedFail: MovableNodeSimplifiedFail; +declare const movableNodeSimplifiedPass: MovableNodeSimplifiedPass; + +expectError(movableNodeSimplifiedFail); +expectType(movableNodeSimplifiedPass); + +const movablePosition = { + top: 42, + left: 42, + move(position: Position) { + return position; + }, +}; + +const movableNode = { + position: movablePosition, + top: {position: movablePosition, size}, + left: {position: movablePosition, size}, +}; + +expectType(movableNode); + +// Should exclude `Function` and `Size` type (mainly visual, mouse over the statement). +type ExcludeFunctionAndSize1 = ConditionalSimplifyDeep; +expectType(movableNode); + +// Same as above but using `IncludeType` parameter (mainly visual, mouse over the statement). +type ExcludeFunctionAndSize2 = ConditionalSimplifyDeep; +expectType(movableNode);