Here are some TypeScript type definitions to consider:
enum Environment {
Local = 'local',
Prod = 'prod'
}
type EnvironmentConfig = {
isCustomerFacing: boolean,
serverUrl: string
}
type DefaultBaseConfig<T> = {
default: T
}
type EnvironmentBaseConfig<T> = {
[key in Environment]: T
}
type BaseConfig<T> = DefaultBaseConfig<T> | EnvironmentBaseConfig<T>;
// const baseConfig: ??? = {
const baseConfig: BaseConfig<Partial<EnvironmentConfig>> = {
default: {
isCustomerFacing: false
},
local: {
serverUrl: 'https://local.example.com'
},
prod: {
isCustomerFacing: true
}
};
There's an object called baseConfig
, but it needs refinement. I want each environment-keyed property to be a Partial<EnvironmentConfig>
, and the default
property to also require that when combined with any environment, they must form a full EnvironmentConfig
.
In this example, local
works because its properties align with default
. However, prod
doesn't work because it lacks the necessary serverUrl
property.
This config object will later be merged conditionally using TypeScript constraints to ensure runtime functionality. I've tried various approaches, like conditional types, but haven't found a solution yet.
Is there a way to achieve this goal effectively?
Below is my current attempt, which hasn't yielded the desired results:
type SplitWithDefault<
TComplete,
TDefault extends Partial<TComplete>,
TSplit extends { default: TDefault, [key: string]: Partial<TComplete> }
> = { default: TDefault }
& { [P in keyof Omit<TSplit, 'default'>]: (TSplit[P] & TDefault) extends TComplete ? TSplit[P] : never };