Consider following code:
type Name = 'foo' | 'bar'
type ProviderFuncFromString = (data: string) => { name: Name; data: string }
type ProviderFuncFromNumber = (numb: number) => { name: Name; data: string }
function providerFuncFromStringFactory(name: Name): ProviderFuncFromString {
return (data) => ({ name, data })
}
function providerFuncFromNumberFactory(name: Name): ProviderFuncFromNumber {
return (numb) => ({ name, data: numb.toString() })
}
type ProviderMap = {
foo: ProviderFuncFromString
bar: ProviderFuncFromNumber
}
const providerMap: ProviderMap = {
foo: providerFuncFromStringFactory('foo'),
bar: providerFuncFromNumberFactory('bar'),
}
There are two duplications here:
- Both
ProviderMap
andproviderMap
say that the provider func forfoo
is from string, and that provider func forbar
is from number - In
providerMap
the keysfoo
andbar
are duplicated both in the key and value.
How can I remove both of these duplications?
I don’t want to use any manual type assertions like as
keyword as I want to maintain type-safety provided by TypeScript.
I know that for simple string union types I can deduplicate type
and const
like this:
export const NameVal = ['foo', 'bar']
export type NameType = (typeof NameVal)[number]
Here foo
and bar
are defined only once, providing us both with type
and runtime const
.
However, I don’t know how to apply this “trick” to more advanced types.
I tried e.g.:
type providerMapType = (typeof providerMap)[keyof typeof providerMap]
but then the type becomes
type providerMapType = ProviderFuncFromString | ProviderFuncFromNumber
and not the desired
type providerMapType = {
foo: ProviderFuncFromString
bar: ProviderFuncFromNumber
}
As for the duplication within providerMap
of foo
and bar
, I was trying something like this:
function getFromStringFactory(name: Name): [Name, ProviderFuncFromString] {
return [name, providerFuncFromStringFactory(name)]
}
function getFromNumberFactory(name: Name): [Name, ProviderFuncFromNumber] {
return [name, providerFuncFromNumberFactory(name)]
}
const providerMap2 = Object.fromEntries([
getFromStringFactory('foo'),
getFromNumberFactory('bar'),
])
but here providerMap2
evaluates to const providerMap2: any
.