Reputation: 3322
I defined a union type called IConfigFactory
, it accepts a parameter of type IConfig
or returns a function of type IConfig
。
but it not do type inference correctly, like as follows:
interface IViteConfig {
vite?: true;
}
interface IWebpackConfig {
vite?: false;
plugins: string[];
}
type IConfig = IViteConfig | IWebpackConfig;
type IConfigFactory = (() => IConfig) | IConfig;
function defineConfig(config: IConfigFactory) {
return config;
}
defineConfig({
vite: true, // Not prompting as expected
plugins: [],
});
type IConfigFactory2 = IConfig;
function defineConfig2(config: IConfigFactory2) {
return config;
}
defineConfig2({
vite: true,
plugins: [], // Prompting as expected
});
but when I defined IConfigFactory
only accepts a parameter of type IConfig
, it works well.
What am I doing wrong and how can I fix it?
Upvotes: 1
Views: 214
Reputation: 8327
Based on comments, use an explicit discriminator rather than relying on vite
being true or false. If you think about it, that approach is fragile anyway, as it defines all config in terms of vite. Maybe you want to add Rollup or Tsup, etc.
interface ViteConfig {
kind: 'vite'
}
interface WebpackConfig {
kind: 'webpack'
plugins: string[]
}
TypeScript discriminated unions work by being specific about a 'discriminator'. In your case, vite
:
interface IViteConfig {
vite: true; // <--- NOTE
}
interface IWebpackConfig {
vite: false; // <--- NOTE
plugins: string[];
}
type IConfig = IViteConfig | IWebpackConfig;
type IConfigFactory = (() => IConfig) | IConfig;
function defineConfig(config: IConfigFactory) {
return config;
}
defineConfig({ vite: true }); // ok
defineConfig({ vite: false, plugins: [] }); // ok
As a side note, the I
prefix, commonly used in old school WIN32 and C#-land is overkill in TypeScript.
The same code without the I
's is more 'ergnomic' (though this is subjective, of course:
interface ViteConfig {
vite: true;
}
interface WebpackConfig {
vite: false;
plugins: string[];
}
type Config = ViteConfig | WebpackConfig;
type ConfigFactory = (() => Config) | Config;
function defineConfig(config: ConfigFactory) {
return config;
}
defineConfig({ vite: true }); // ok
defineConfig({ vite: false, plugins: [] }); // ok
Upvotes: 1