Default Generics in the Builder Pattern
The thing I removed was the default generic from the TypeSafeStringMap
.
Without the Default Generic
Without the default of an empty object, hovering over the new TypeSafeStringMap
in the example map
shows us this:
constructor TypeSafeStringMap<Record<string, string>>(): Typ
Transcript
0:01 The thing that I changed was I changed this default generic. In other words, I removed it. When I removed it, some interesting things happened.
0:12 We have this map here. If I just remove all of this guff here, this map is now typed as TypeSafeStringMap and the default is Record, string, string. When you don't provide a default, it defaults to this. Now, this type here is tricky, because what we can do is we can set('matt', 'pocock') again.
0:34 This, what it ends up with is Record, string, string and Record, "matt", string. What this means is because keyof TMap is being called here. What you get is, if we say, const result = map.get. Let's say, blah blah blah. The key is typed as string.
0:59 If we have type, let's say, Example = 'matt' or string, then this is going to be resolved as string. The matt disappears inside here. Because get(key: keyof TMap) and the default is a Record string, that little string just there ruins the whole thing, because it means that we don't get typesafety on the get.
1:28 The way to solve this is by adding back that default generic. Now, the first thing that's here will be an empty object. Meaning that, if we don't set anything, then this will be never. This method will be key never because if we look at type Example = keyof this, the keyof empty object is never.
1:55 When you have like this or keyof { matt: string }, for instance, then that's going to win out. The never is going to disappear from the union and you end up with matt here, keyof this. If we go, keyof { brandi: string }, then brandi is going to be added to the thing as well.
2:24 This is basically the importance of this default generic. It gives your builder pattern a TypeSafe place to start. It means that you have to be really, really careful, because that error is pretty hard to debug and it shows how important this default generic is.