-
-
Notifications
You must be signed in to change notification settings - Fork 567
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
Adds type constructors Patch
and ReplaceDeep
#648
Adds type constructors Patch
and ReplaceDeep
#648
Conversation
It works as expected @ahrjarrett ! 🎉 I appreciate the effort you've put into this. About the naming, One idea could be to rename it to Before we move forward with the name change, could you elaborate on potential scenarios where users might find this beneficial? I'm keen to get your thoughts on this. |
We use the name convention where |
source/deep-undefined-to-null.d.ts
Outdated
@@ -0,0 +1,122 @@ | |||
/** | |||
* Type function that accepts a record, recursively removes all optional property modifiers, and in those properties' values replaces `undefined` with `null`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't use the *
prefix for doc comments. See other types here.
Could you tell me more about your use case? To be honest I picked this up because it sounded fun, but I'm still a little fuzzy on where this type might be useful. At first I figured this would be useful because
I mean it depends on the use case right -- I've worked some places where undefined properties are stripped away entirely (the worst option, IMO), and I've worked places where something like The reason I was thinking to support that kind of configuration is because this is a lossy transformation. By which I mean, once the optional properties are patched, we can't recover which properties were optional. You have to do both in one pass: 1) patch the optional properties, and 2) replace them with some other type. |
As far as what to name this type, I've been kicking it around, and I keep coming back to According to MW a patch is
I think what we're doing boils down to exactly that: we're fixing a hole or a weak spot :) |
Also last thought (and then I'm done blowing up this PR) -- I'm actually leaning towards @dawidk92 thoughts? |
…account for new default
Hey @sindresorhus, I requested your review. There are still 2 open questions, would be good to get your take:
Besides those questions, I wanted to make sure there wasn't anything else you wanted changed, that way once we decide, all that's needed is a name change / default argument change |
Thank you all for the hard work and the thoughtful discussion around this topic. @ahrjarrett , the reason I'm interested in this type is related to my work with GraphQL on my backend. In GraphQL, when something is Regarding the name, following the convention mentioned by @sindresorhus , I agree that placing 'Deep' at the end is consistent with the other types in the library. I propose the name I also think it might be wise to require the user to explicitly specify the value they want to replace Your thoughts, @ahrjarrett, @sindresorhus? |
I don't want to design types for imaginary use-cases. Can you think of other types than |
@dawidk92 This needs your opinion. |
I could have done a better job communicating, let me give an example: The reason I don't think But If that all this type did was replace The reason that won't work here is because the main thing that this type does is not replace undefined with another value. Rather, it "fixes" a schema so none of its properties are optional anymore. That's why I proposed the name This fundamentally changes the structure of the type, since all of its properties are guaranteed to be present. The reason this is not a trivial operation is because you lose which fields were patched as soon as you perform that operation. That's why I think this type addresses a real need, is because doing both at the same time is tricky. Using a type like |
Thank you, @sindresorhus and @ahrjarrett, for your insights. @sindresorhus, I understand the need to avoid designing types for imaginary scenarios. Let me provide more context on my use-case, as it's a real-world need. In my backend development with GraphQL, fields that are Regarding your specific question about whether @ahrjarrett, your proposal for However, I must admit that I find the proposed names I appreciate all the work and thought going into this. Please let me know if there's anything else I can do to assist or clarify! |
@sindresorhus any objection to me extracting |
source/replace-optional-deep.d.ts
Outdated
|
||
/** | ||
* TODO: Extract `ReplaceDeep` into a separate module and expose from the top-level | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider writing JSDoc now?
source/replace-optional-deep.d.ts
Outdated
Type, | ||
Find, | ||
Replace, | ||
> = Type extends Find ? Replace |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bikeshedding comments on the naming here. 😁
Suggestion: "Needle", "Haystack"? They're pretty common terms for search/replace.
Also, "Replace" versus "Replacement"? This project has a "Replace" type already. I wouldn't like to see confusion.
test-d/replace-optional-deep.ts
Outdated
expectType<Out4>(test4); | ||
expectType<Out5>(test5); | ||
expectType<Out6>(test6); | ||
expectType<Out7>(test7); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Peanut gallery observation: visually verifying this code is going to require scrolling up and down a lot, particularly if this grows more complex in the future. I would recommend moving the declares and expectTypes closer to the types you're actually testing. But if the project owners disagree, I would go with their recommendation.
Not-so-peanut-gallery observation: what happens if someone puts the find type inside the replacement type? Let's come up with some evil replacement testcases too, including:
- complex types for the replace value
never
unknown
any
- a value already in the type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ajvincent thank you for your review.
I appreciate you putting thought into how recursion could misbehave if the replacement itself contained part(s) that matched the needle. The nice thing about implementing find/replace this way (handling a positive match as a base case) is that you avoid that complexity altogether.
I added extra tests for Patch
, which I'll be requesting your review on in the next day or 2. But wanted to answer your concern while it was fresh on my mind -- since a match is treated as a base case, and we don't continue recursing, users can use any type they like: any
, never
, a copy of the entire tree -- because we consider that to be the base case, we will never traverse down that subtree.
… @ahrjarrett/deep-undefined-to-null
…om/ahrjarrett/type-fest-fork into @ahrjarrett/deep-undefined-to-null
I pushed up some changes. I still have to go back and rewrite things to A) remove old code, and B) rewrite things to be consistent with the rest of type-fest. The main changes include:
I'm pretty happy with how I'll finish #1 and #2 hopefully tomorrow after work, but wanted to push something up to make it clear that I haven't abandoned this work |
… that contains an optional prop doesn't interfere with the base case
…output type doesn't grow infinitely, we know we're not recursing down subtrees of matching exprs
How are we looking? I'm reluctant to review a patch that isn't passing all the tests. |
… @ahrjarrett/deep-undefined-to-null
DeepUndefinedToNull
typePatch
and ReplaceDeep
I've done the hard part of cleaning this up. Here's a playground so you can interact with it if that's helpful: I still need to figure out how to (hopefully programmatically) clean up the linting errors (over 700 of them). I did not see an easy way to run eslint with the Before I spend the time to do that, it would be helpful to get some feedback so I don't have to do it twice. Edit: tagging @sindresorhus @ajvincent |
|
My apologies for not reviewing yet... still failing tests, and GitHub is no longer retaining the logs. |
Closing as this is not moving forward. |
Closes #641
The implementation includes a generic, perhaps more useful type
DeepReplace
that I could separate into its own module in a separate PR if you want.Also note that
DeepUndefinedToNull
andDeepReplace
both handle unions the way you'd expect.Not 100% sure about the name
DeepUndefinedToNull
though, since in practice it does more than that (since replacing partial properties withnull
is configurable, and since it also functions as a kind ofDeepRequired
.Happy to make any requested changes!