-
-
Notifications
You must be signed in to change notification settings - Fork 49
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
Fix type errors for Button's use of Icon data. #125
Conversation
🦋 Changeset detectedLatest commit: 84ea64b The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
PR Summary
|
0753489
to
9187780
Compare
Just found something to improve with this, so don't merge it quite yet. |
9187780
to
b40c033
Compare
Okay, now the PR introduces inclusive input types for supplying icon data as arguments for components whether it be a null, undefined, string, IconDefinition, or an object with all the properties of an Icon component (which should include Icon components themselves). Then it extracts the |
b40c033
to
640306c
Compare
Amended the changeset to provide more information due to the above changes. |
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.
This looks great, and a huge win. Just a few minor notes
} | ||
|
||
function isIconComponentProps(v: any): v is ComponentProps<Icon> { | ||
return typeof(v) === "object" && v && typeof(v['classes']) === "object" && "path" in v; |
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.
There is a isLiteralObject() util that could help refine this a little.
I'm also not sure typeof(v['classes']) === "object" && "path" in v
is the correct check.
I wonder if asIconData()
should just be...
export function asIconData(v: IconInput): IconData {
return ('data' in v) ? v.data : v;
}
I might be overlooking something though.
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.
There is a isLiteralObject() util that could help refine this a little.
I agree, that's better to use.
I'm also not sure
typeof(v['classes']) === "object" && "path" in v
is the correct check.
When I was writing this initially, I had thought that ComponentProps<Icon>
required an object with all the same properties of Icon
, but I can see now that it actually is like a Partial
wrapper around that, so yeah, this definitely would NOT be the right check. Good catch.
Since IconDefinition
has a more consistent structure & because it's the only other supported "object" type, we should just check to see if the given v
object does NOT have certain properties that all IconDefinition
objects are required to have and that Icon
lacks. Therefore, a single property check can, by process of elimination, validate which type we're dealing with.
I wonder if asIconData() should just be...
In theory, you'd be right (if it were just JS), but in order for TypeScript to rule out the possibility that the IconInput
parameter is actually a ComponentProps<Icon>
, you either have to exhaustively check all possible properties (far more work than is actually necessary) or write a function that forcibly coerces the type understanding via a type guard return value (<param> is <type>
return 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.
fwiw, there is a typeGuards utils with hasProperty()
, etc. Might not be useful here
--- | ||
|
||
Added new `IconInput` and `IconData` types to enable inclusive & seamless passing of icon arguments between components. Also provides a `asIconData` utility function for type-safe conversion. | ||
Fixed type errors for Button & TextField's use of Icon data. |
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.
<Icon>
used to just accept <Icon path="...">
...then it was extended to allow <Icon svg="...">
, ```
...then it was extended to have a simple <Icon data="...">
prop and infer which of the props was intended (path, svg, svgUrl), so I call that a feature to allow TextField
to take in more options than just path
:).
svgUrl
is quite useful when your loading over the network, and not populating your bundle with all potential options.
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.
Hehe, I wasn't saying that it doesn't support different kinds of inputs. More that, we have instances of wrappers with inner wrappers with inner-inner Icons, etc. and the allowed data types for those wrapper components' distinct arguments do not always match, leading to inconsistency that has the potential to force type errors for no reason. I actually have to compliment this project for the diversity of data formats that Icon
supports.
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.
:). It's been battled tested in a lot of use cases. One thing I struggle with is getting the time to show all the niceties in examples :). <Table>
is under represented for how much it packs ;)
export type IconInput = ComponentProps<Icon>['data'] | ComponentProps<Icon>; | ||
export type IconData = ComponentProps<Icon>['data']; | ||
|
||
export function asIconData(v: IconInput): IconData { |
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.
What if we call this getIconData()
?
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.
I considered that initially, and I'm good with that if you prefer it. Only reason I chose as
was because get
sounded like it was consistently extracting data, but just in potentially different/abstracted ways whereas as
communicates that the object in question might already be the return type (IconData
) and conveys that the function's purpose is ultimately type conversion rather than extraction. Up to you.
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 can leave it as
is (pun intended) :)
640306c
to
ee76171
Compare
ee76171
to
a71317a
Compare
a71317a
to
84ea64b
Compare
No description provided.