[Typescript] Default value for Builder pattern - 04 (keyof {} -> never)
From previous post, Builder pattern - 03
If we do the following changes:
- class TypeSafeStringMap<TMap extends Record<string, string> = {}> {
+ class TypeSafeStringMap<TMap extends Record<string, string>> {
It has a big impact of the codebase:
When we have the default value:
class TypeSafeStringMap<TMap extends Record<string, string> = {}> {}
const map = new TypeSafeStringMap()
// ^? TypeSafeStringMap<{}>
When we don't have the default value:
class TypeSafeStringMap<TMap extends Record<string, string>> {}
const map = new TypeSafeStringMap()
// ^? TypeSafeStringMap<Record<string, string>>
So, when we set/get the value, we will see the differences
class TypeSafeStringMap<TMap extends Record<string, string> = {}> {}
const map = new TypeSafeStringMap().set('name', 'abc')
// map: TypeSafeStringMap<Record<"name", string>>
class TypeSafeStringMap<TMap extends Record<string, string>> {}
const map = new TypeSafeStringMap().set("name", "abc")
// ^? TypeSafeStringMap<Record<string, string> & Record<"name", string>>
Take a close look of difference:
// with default {}
TypeSafeStringMap<Record<"name", string>>
// without default {}
TypeSafeStringMap<Record<string, string> & Record<"name", string>>
When we have getter function:
get(key: keyof TMap): string {
return this.map[key];
}
// without default value
type a = keyof Record<"name", string>
// ^? "name"
type b = keyof (Record<string, string> & Record<"name", string>)
// ^? string
b
is type string
, because string | "name"
results in string
type.
Why a
work fine, this is because
type c = keyof {}
// ^? never
type d = never | "name"
// ^? "name"
Tips:
type isNever<T> = [T] extends [keyof {}] ? true: false;
type e = isNever<never> // true
type f = isNever<false> // false