-
Notifications
You must be signed in to change notification settings - Fork 113
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
TypeScript Type Traps #1149
TypeScript Type Traps #1149
Conversation
c6a4a87
to
bc9ce6e
Compare
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.
Thanks! I appreciate that we try to use typescript properly 😃
@@ -75,7 +75,11 @@ export class UserDetailsComponent implements OnInit, OnDestroy { | |||
// TODO (tc) This needs to be refactored to resemble our other patterns | |||
// for specific object pages. | |||
// combineLatest depends on the user object existing already. | |||
this.user = <User>{}; | |||
this.user = { |
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.
Worth changing the user interface to something like
interface User {
id?: string;
name?: string;
membership_id?: string;
}
...so we could not set them to empty strings here?
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.
Here is my reasoning: in any strongly-typed language, if you have a User
object that contains a property name
, you know with certainty that the property user.name
exists. But with the original code (user = <User>{};
), or with your suggested change, we no longer have that certainty. That may or may not have any practical impact; referencing the non-existent user.name
would return undefined instead of an empty string. For a non-string field, it becomes more problematic. Say we had a type field that could be 'a' or 'b' only, but making it optional wouldn't undefined also need to be checked? That seems akin to the Worst Mistake in Computer Science (e.g. https://bit.ly/1JHIqbY), no? TypeScript lets us be tolerant, though, as you have suggested... but should we be? Not sure. 🤷♂
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.
in any strongly-typed language, if you have a User object that contains a property name, you know with certainty that the property user.name exists.
...but if we set user.name
to ''
, that certainty isn't worth much: we don't know if it's a valid, proper username -- we only know it's not undefined
. That's not the kind of certainty I appreciate 😄
That said, there's surely better ways than what I have proposed. Akin to a Maybe
or Optional
type, we might be able to say User | undefined
. I've never done a deep dive into how to do this in TS The Right Way; I'm vaguely aware that there's a project called ts-fp
that provides Option
.
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.
Why does this.user
need to be initialized here?
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.
@bcmdarroch: I was trying to answer that very question above; sorry if not clear. In a nutshell:
- If you have something that is a User object, you might assign null to it, in which case it certainly does not have an id, name, etc.
- But if it is non-null, I do not believe one should have to wonder (read: check or worse, blow-up because you did not check) if it has a property named
id
. It is a User object: it should always have that property. Now, whether that property itself is initialized--to @srenatus 's point--is worth considering, too, but I'm just taking the first, small step here.
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.
but I'm just taking the first, small step here.
Let's not spread this, but keep trying to improve it in each step, then 😃
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.
👍 👍
Signed-off-by: michael sorens <msorens@chef.io>
Signed-off-by: michael sorens <msorens@chef.io>
bc9ce6e
to
8373d4c
Compare
Apply types and destructured types Signed-off-by: michael sorens <msorens@chef.io>
Signed-off-by: michael sorens <msorens@chef.io>
Does not really make sense to have a user or a team object that is totally empty. Their properties might be empty, but their properties should always exist. This nicely cleans up the templates, too. Signed-off-by: michael sorens <msorens@chef.io>
8373d4c
to
d0e3f1e
Compare
🔩 Description: What code changed, and why?
On the periphery of some UI work I noticed some type safety anomalies where things that should have been compiler errors (or so I thought) were not flagged as such. Upon refreshing myself on how static typing, type inference, and type assertions actually work in TypeScript I made some surprising discoveries. This PR fixes an assortment of such issues.
They are grouped by commit into several violation categories, so follow the commits!
Here is one startling example:
After entering an invalid value (
CUSTOMxxx
) for a type, the error is not flagged in VSCode until one removes the type assertion:⛓️ Related Resources
Alas, these types of potential pitfalls are not going to be noticed by the computer no matter how much linting you do. So I wrote up some slides ("TypeScript Type Traps") to document and explain these for the team's reference moving forward.
Please read TypeScript Type Traps
👍 Definition of Done
A more type-safe UI codebase.
👟 How to Build and Test the Change
Rebuild automate-ui