-
Notifications
You must be signed in to change notification settings - Fork 0
Chapter 6 #6
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
base: initial
Are you sure you want to change the base?
Conversation
// ^? | ||
``` | ||
|
||
In this case, the `age` property resolves to `never` because it's impossible for a single property to be both a `number` and a `string`. |
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 it's only the age
property that resolves to never
- the type itself, its potential other properties, etc wont be never-ified. That makes the previous definition that you coined above inaccurate:
What type do you think
StringAndNumber
is? It's actuallynever
. This is becausestring
andnumber
have innate properties that can't be combined together.
In that case it's not about mismatched properties but about completely disjoint domains (or something ;p). Like, there is maybe some overlap between both of those that could be the output of such operation. For example, both number
and string
have { toString(): string }
. For practical purposes, it's rather useless to create an object with only compatible methods though.
} | ||
``` | ||
|
||
This is very different because it actually sources an error. With intersections, TypeScript will only raise an error when you try to access the `age` property, not when you define it. |
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.
With intersections, TypeScript will only raise an error when you try to access the
age
property, not when you define it.
It won't even raise an error - it will just resolve it to never
and since never
is assignable to everything... this might silently creep issues into ur code.
|
||
When you're working in TypeScript, the performance of your types should be at the back of your mind. In large projects, how you define your types can have a big impact on how fast your IDE feels, and how long it takes for `tsc` to check your code. | ||
|
||
`interface extends` is much better for TypeScript performance than intersections. With intersections, the intersection is recomputed every time it's used. This can be slow, especially when you're working with complex types. |
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.
Intersections are heavily cached too. Some resolutions sometimes (maybe? I'm not sure) can be recomputed but in many cases, they won't.
|
||
Interfaces are faster. TypeScript can cache the resulting type of an interface based on its name. So if you use `interface extends`, TypeScript only has to compute the type once, and then it can reused it every time you use the interface. | ||
|
||
#### Conclusion |
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.
a third reason could be that the IDE preserves the interfaces' names more often than not whereas with type aliases u can often see the content (the intersection) and that might lead to gnarly error messages etc
} | ||
``` | ||
|
||
This version is preferable because, in general, `interface extends` is preferable to intersections. |
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 even more important with index signatures because to make sound types all of ur values have to be compatible with applicable index signatures. Intersections won't warn you about this at all.
People often ask to be able to break this rule but that just creates problems: TS playground
acceptAllNonPrimitives(true); | ||
``` | ||
|
||
This means that the `object` type is rarely useful by itself. Using `Record` is usually a better choice. For instance, if you want to accept any object type, you can use `Record<string, unknown>`. |
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 is a fallacy - to the best of my knowledge there is no way clear way to create a type that would represent "a plain object". Record<string, unknown>
has its gotchas too and it just doesn't deliver that: TS playground
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 only kinda works with objects defined type
because those have "inferrable index signatures" - which is far from obvious
const scores: Record<string, number> = {}; | ||
``` | ||
|
||
#### Solution 2: Default Properties with Dynamic Keys |
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.
calling them default feels weird to me - i'd use known/concrete
>; | ||
``` | ||
|
||
Now TypeScript will throw an error when the object includes a key that doesn't exist in `Environment`, like `notAllowed`. |
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 is not the only place when a language like this is used but I wonder if it wouldn't be better to adjust all of them to use "report an error" over "throw an error".
updateAlbum({ title: "Geogaddi", artist: "Boards of Canada" }); | ||
``` | ||
|
||
### `Required` |
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 might be neat to mention somewhere that Partial/Required are not limited to objects - they work fine with tuples as well :)
type DistributiveOmit<T, K extends PropertyKey> = T extends any | ||
? Omit<T, K> | ||
: never; |
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.
or :p
type DistributiveOmit<T, K extends PropertyKey> = T extends any | |
? Omit<T, K> | |
: never; | |
type DistributiveOmit<T, K extends PropertyKey> = { [P in keyof T as Exclude<P, K>]: T[P]; } |
|
||
`Omit` will mean that the object grows as the source object grows. `Pick` and `interface extends` will mean that the object will stay the same size. So depending on requirements, you can choose the best approach. | ||
|
||
#### Solution 2: Updating a Product |
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.
Usually, your Solution X were interchangeable but, in here, this one feels like an extension of the previous one (like an extra step). The previous one doesn't mention Partial
at all (which is a requirement 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.
hm, maybe this just isn't true? I see now that Solution X is meant to correspond to Excercise X. I was under impression that before you were using multiple Solutions to a single Excercise at times but maybe those were always presented under subheaders or smth
No description provided.