-
-
Notifications
You must be signed in to change notification settings - Fork 571
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
Breaking change to Simplify<>
introduced in v2.16.0
#436
Comments
// @skarab42 |
@andyjy I would love to have an idea of what type is causing the bug. I think I can see what's wrong (but that's just a supposition without a reproduction). I'll make a temporary fix that you can test. Then we'll see what to do from there. |
@andyjy Just an idea, you add the type in question to a file, then compile it and get the full type in a |
Thanks @skarab42 - great thinking, unfortunately the change in the PR #437 does not solve this issue for me (still same errors). Separately I confirmed my error is caused by the use of This increases my confidence in the point in my original post that - unless I'm missing something? - the use of I will try spend some time to extract the offending type(s) in question as you suggest - in short I'm wrapping diff --git a/node_modules/type-fest/source/set-required.d.ts b/node_modules/type-fest/source/set-required.d.ts
index a62da3b..894eabd 100644
--- a/node_modules/type-fest/source/set-required.d.ts
+++ b/node_modules/type-fest/source/set-required.d.ts
@@ -1,5 +1,5 @@
import type {Except} from './except';
-import type {Simplify} from './simplify';
+import type {SimplifyObject} from './simplify';
/**
Create a type that makes the given keys required. The remaining keys are kept as is. The sister of the `SetOptional` type.
@@ -27,7 +27,7 @@ type SomeRequired = SetRequired<Foo, 'b' | 'c'>;
@category Object
*/
export type SetRequired<BaseType, Keys extends keyof BaseType> =
- Simplify<
+ SimplifyObject<
// Pick just the keys that are optional from the base type.
Except<BaseType, Keys> &
// Pick the keys that should be required from the base type and make them required.
diff --git a/node_modules/type-fest/source/simplify.d.ts b/node_modules/type-fest/source/simplify.d.ts
index 7112dc3..cb4f166 100644
--- a/node_modules/type-fest/source/simplify.d.ts
+++ b/node_modules/type-fest/source/simplify.d.ts
@@ -75,9 +75,12 @@ fn(someInterface as Simplify<SomeInterface>); // Good: transform an `interface`
@category Object
*/
+export type SimplifyObject<T> = {[KeyType in keyof T]: T[KeyType]};
+
export type Simplify<
AnyType,
Options extends SimplifyOptions = {},
> = Flatten<AnyType> extends AnyType
? Flatten<AnyType, Options>
: AnyType;
+// ^- this time retaining the updated version of Simplify<> from https://github.com/sindresorhus/type-fest/pull/414 |
@andyjy Thank you for testing and don't waste time with the extraction of the type if it is too complicated. In fact the change was made in anticipation of this PR #408 (comment) |
Thank you - switching the conditional to This is curious as now (Both errors are still resolved if I revert back to the second patch I posted above that uses the original simple implementation for |
@andyjy Just one more test 🙏, I need to understand what is going on and where the problem is. Can you patch with the code below and test with export type OldSimplify<T> = {[KeyType in keyof T]: T[KeyType]};
export interface SimplifyOptions {
deep?: boolean;
}
export type Flatten<
AnyType,
Options extends SimplifyOptions = {},
> = Options['deep'] extends true
? {[KeyType in keyof AnyType]: Simplify<AnyType[KeyType], Options>}
: {[KeyType in keyof AnyType]: AnyType[KeyType]};
export type Simplify<
AnyType,
Options extends SimplifyOptions = {},
> = AnyType extends Function ? AnyType : Flatten<AnyType, Options>; |
With the above code (if you don't use the deep option) I don't see how it's different from calling the original version? and how it could cause an excessively deep instantiation. |
This comment was marked as outdated.
This comment was marked as outdated.
In fact - introducing any conditional seems to cause the error, e.g.:
I can't explain why, but evidently the compiler is not simply optimising away this simple conditional. Unless anyone has a better understanding of why this is, I would suggest proceeding by reverting to the original definition of ~~
I subsequently realised |
@andyjy Thanks for testing. @sindresorhus I think Simplify is a UX tool and should not be used internally. For me it is up to the user to apply Simplify (or SimplifyDeep) where they need it. At the moment there are scenarios where Simplify is called redundantly when using multiple types that use it internally. |
@skarab42 Some of the types IMHO do need |
I think we should split up Make:
But keep the old |
@sindresorhus While doing the PR, I realise that I don't see how not to introduce a breaking change. If we export the old Simplify which had no options, new users that use the deep option will have to change their code (I think this number is close to zero). And we can't keep the existing signature, as tested by @andyjy , because introducing any condition produces an error in his IDE (which I still don't understand, I would really like to reproduce the error). In any case I would like to reduce the number of conditions even if it means having several signatures, that's what I propose.
// The old one
export type Simplify<Type> = {[KeyType in keyof Type]: Type[KeyType]};
// The old one but deep
export type SimplifyDeep<Type> = {[TypeKey in keyof Type]: SimplifyDeep<Type[TypeKey]>};
export type ConditionalSimplify<Type, ExcludeType> = Type extends ExcludeType
? Type
: {[TypeKey in keyof Type]: Type[TypeKey]};
export type ConditionalSimplifyDeep<Type, ExcludeType> = Type extends ExcludeType
? Type
: {[TypeKey in keyof Type]: ConditionalSimplifyDeep<Type[TypeKey], ExcludeType>}; The conditional version is much more flexible than the current version because the user can specify the type(s) he does not want to simplify and the total number of conditions is greatly reduced by removing the options. |
@skarab42 Alright. Let's do what you propose, but let's keep ConditionalSimplify internal for now. It would give us a chance to try it out and fix any possible issues. Long-term I would like for |
@sindresorhus I have made an TS Playground that illustrates a part of the problem in case you want to report it to the TS team. |
The release is out: https://github.com/sindresorhus/type-fest/releases/tag/v3.0.0 |
The change to
Simplify<>
introduced in #414 looks great, but turns out it breaks my codebase vs the prior definition ofSimplify<>
, in two ways:Type instantiation is excessively deep and possibly infinite
errors in the VSCode typescript checkerRangeError: Maximum call stack size exceeded
fatal error when runningtsc
I would love to create a Minimum Reproducible Example but unfortunately the types involved span a bunch of files and types (as suggested by the nature of these errors) so that would be quite a challenge.
I've confirmed 100% this is the cause and created a workaround by patching back to the prior
Simplify<>
definition using patch-package and the patch below.I also appreciate this error isn't caused by
type-fest
alone - only occurring in combination with my own code - so I can see some may not consider this strictly a breaking change. But I figured the fact the breakage only came with the update totype-fest
seemed worth sharing - and potentially revertible.In terms of a potential solution - I see that:
Simplify
handle all types #414 is to improve the behaviour ofSimplify<>
when applied to non-object types, andSimplify<>
is used "internally" within othertype-fest
types (e.g.Merge<>
,SetOptional<>
,Spread<>
,Writable<>
) where the input types are expected to be objects and thus the improvement implemented by Fix:Simplify
handle all types #414 shouldn't be applicable(?)Would it therefore make sense to revert #414 and reimplement using two separate types in a way such that the original "simple"
Simplify<>
definition is used by these internal usages?E.g. re-add the original
Simplify<>
implementation under another name and update the internal usages to refer to that (where the new functionality to cope with non-object types is not required), leavingtype-fest
users free to pick from either type as appropriate?If this sounds good I could provide a PR - I figured to share the issue & check for feedback first.
Thanks!
patch-package
patch I have currently used to revert this change:The text was updated successfully, but these errors were encountered: