Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.0.0] built-in .d.ts is missing some(or many) types from @types/webpack #11630

Closed
AviVahl opened this issue Oct 10, 2020 · 70 comments
Closed

Comments

@AviVahl
Copy link

AviVahl commented Oct 10, 2020

Bug report

Encountered this while trying to upgrade my loader to webpack@5.
In webpack@4, with @types/webpack, I could create a fully typed loader.
In webpack@5, there's aren't any exported types for the Loader or the LoaderContext

What is the expected behavior?

Auto generate (?) or just include these interfaces:
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/6a1c6a5567fa177396fa802b2367ec34e1028eea/types/webpack/index.d.ts#L2116

Other relevant information:
webpack version: 5.0.0
Node.js version: 14.13.1
Operating System: Fedora 32
Additional tools:

@AviVahl
Copy link
Author

AviVahl commented Oct 10, 2020

Also, EntryObject is not exported (found when upgrading a different project).

@AviVahl
Copy link
Author

AviVahl commented Oct 10, 2020

Also, the type for generated for RecursiveArrayOrRecord in DefinePlugin is missing null | undefined.

Source says:

/** @typedef {null|undefined|RegExp|Function|string|number|boolean|bigint|undefined} CodeValuePrimitive */

(it has undefined listed twice, not that it should matter)

.d.ts says:

type RecursiveArrayOrRecord =
	| string
	| number
	| bigint
	| boolean
	| Function
	| RegExp
	| RuntimeValue
	| { [index: string]: RecursiveArrayOrRecord }
	| RecursiveArrayOrRecord[];

Is the .d.ts being generated with strictNullChecks: true?

@vitoyucepi
Copy link

I miss some types from @types/webpack too.

  1. MultiConfigurationFactory
  2. Plugin
  3. Optimization

@AviVahl maybe you should rewrite title like this?

[5.0.0] built-in .d.ts is missing some(or many) types from @types/webpack

@AviVahl AviVahl changed the title [5.0.0] built-in .d.ts is missing loader types (that @types/webpack had) [5.0.0] built-in .d.ts is missing some(or many) types from @types/webpack Oct 11, 2020
@AviVahl
Copy link
Author

AviVahl commented Oct 11, 2020

Done. I've encountered the Plugin one as well. I believe it's now called PluginInstance in the built-in .d.ts.

@AviVahl
Copy link
Author

AviVahl commented Oct 11, 2020

The options parameter of stats.toJson and stats.toString was properly typed in @types/webpack. now it's any.

@vitoyucepi
Copy link

A lot of @types packages uses Plugin. They have to replace Plugin with WebpackPluginInstance.

@snitin315
Copy link
Member

I think you can send a PR for exporting more types as per your need. Types can be exposed by adding @typedefs to lib/index.js. Run yarn special-lint-fix to update the typings.

@fregante
Copy link

fregante commented Oct 11, 2020

@snitin315 why are these types not part of the webpack package? Feels like it's the easier way to keep them in sync since webpack does have some types already.

@kavsingh
Copy link

kavsingh commented Oct 11, 2020

to add, also missing ConfigurationFactory which was available in @types/webpack

@alexander-akait
Copy link
Member

Yes, feel free to send a PR with fixes

@alexander-akait
Copy link
Member

Just for information, original loader interface:

interface Loader extends Function {
            (this: LoaderContext, source: string | Buffer, sourceMap?: RawSourceMap): string | Buffer | void | undefined;

            /**
             * The order of chained loaders are always called from right to left.
             * But, in some cases, loaders do not care about the results of the previous loader or the resource.
             * They only care for metadata. The pitch method on the loaders is called from left to right before the loaders are called (from right to left).
             * If a loader delivers a result in the pitch method the process turns around and skips the remaining loaders,
             * continuing with the calls to the more left loaders. data can be passed between pitch and normal call.
             */
            pitch?(remainingRequest: string, precedingRequest: string, data: any): any;

            /**
             * By default, the resource file is treated as utf-8 string and passed as String to the loader.
             * By setting raw to true the loader is passed the raw Buffer.
             * Every loader is allowed to deliver its result as String or as Buffer.
             * The compiler converts them between loaders.
             */
            raw?: boolean;
        }

I think we need improve this

@kryops
Copy link

kryops commented Oct 20, 2020

As of 4.41.23, @types/webpack also provides the WebpackPluginInstance interface, so type definitions for plugins can be migrated over to support webpack 5, while still maintaining compatibility to @types/webpack.

Adding the Plugin definition in webpack's own types as well (as done in #11698) should allow for a smoother migration path.

@jasonwilliams
Copy link
Contributor

Output no longer works since upgrading a plugin to Webpack 5.
I can see it in the types.d.ts file but maybe its just not exported?

@lorenzodallavecchia
Copy link

I also found some issues with the new types.

  • stats.toJson now returns any, removing a lot of type safety (this is a big deal for me).
  • InputFileSystem and OutputFileSystem are not exported and also they are not compatible with fs from @types/node.
  • OptimizationSplitChunksCacheGroup.test accepts a generic Function instead of typing its callback.

@alexander-akait
Copy link
Member

Yep, feel free to send a fix

@martinbroos
Copy link

I'm encountering that WatchOptions is not exported but has the correct type. So please add to exports.

@alexander-akait
Copy link
Member

alexander-akait commented Nov 19, 2020

Configuration["watchOptions"]?

@martinbroos
Copy link

@evilebottnawi thanks that works, didn't know this was an option.

@lorenzodallavecchia
Copy link

@evilebottnawi thanks for the tip.

We can push that even further, extracting the return values of functions by using ReturnType.
This is an excerpt from a file that I'm using in my project to get back some of the missing types.

export type Watching = ReturnType<Compiler["watch"]>;
export type WebpackLogger = ReturnType<Compilation["getLogger"]>;

I still think that a coherent typings file should export the entire surface of names that are used to define the "top-level" types. However, these techniques mitigate the issue somewhat.

@alexander-akait
Copy link
Member

we will focus on types in near future, anyway you can send a PR to improve this

@alexander-akait
Copy link
Member

To be honestly, webpack don't know this stuff, because only object allowed, function/promises allowed by webpack-cli so you can use:

import type { Configuration } from 'webpack';

export default (env: Record<string, any>, args: Record<string, any>): Configuration => {
  return { mode: 'production' }
}

Don't know why these types was added to webpack types + they are wrong.

@astorije
Copy link

Any chance this can be added in the core types directly as a ConfigurationFactory? That would be super helpful!
Even better if args is { mode?: Configuration['mode'] } & Record<string, any>

@alexander-akait
Copy link
Member

args is Record<string, any>, you can improve this on your side, because it is based on passed CLI args

@MarkKoester
Copy link

Is that something that should be provided from the CLI side of things or is the idea that those types are out of webpack's (or the CLI's) scope until it's given as a configuration object?

@astorije
Copy link

astorije commented May 11, 2021

args is Record<string, any>, you can improve this on your side, because it is based on passed CLI args

I think mode?: Configuration['mode'] records that correctly since the CLI does accept an optional mode, but if anything
ConfigurationFactory could then be ConfigurationFactory<T = any, U = any> so that we could set it with ConfigurationFactory<never, { mode?: Configuration['mode'] }>.

@alexander-akait
Copy link
Member

It is not possible to supply the correct type between passed args in CLI (i.e. webpack --mode production) and types for config function, so, arg and env is always Record<string, any>

@alexander-akait
Copy link
Member

How we can detect mode? We have 1+k options for CLI, run webpack --help=verbose

@astorije
Copy link

Are you saying mode can be of a different type besides (optional) string?

@alexander-akait
Copy link
Member

I don't understand your problem, args is Record<string, any>, you can use const myMode = args['mode'] as Configuration['mode']

@astorije
Copy link

And I don't understand why mode can't be string | undefined when the doc itself says so. I do think mode should be typed specifically as it is documented to use a configuration function in order to obtain argv.mode (see bottom of the page linked).

It is generally bad TS practice to use any or as when alternatives are possible as they bypass the type checker (and even more less desirable to recommend it to lib consumers). If you don't want or cannot maintain a list of supported properties, that's totally ok, but in that case ConfigurationFactory should be a generic so that consumers can set the corresponding types like you suggested :)

I think something like the following would make people needing this happy (but would love feedback from others, /Cc @vitoyucepi, @kavsingh, @MarkKoester):

import type { Configuration } from 'webpack';

export type ConfigurationFactory<T, U> = (env: T, args: U) => Configuration;

(I personally would prefer ConfigurationFactory<T, U = { mode?: Configuration['mode'] }> or something like that but if you don't want to maintain that then <T, U> is a good start)

The example from the Webpack doc would become something along the lines of:

import type { ConfigurationFactory } from 'webpack';

const configFactory: ConfigurationFactory<never, { mode?: Configuration['mode'] }> = (env, argv) => {
  if (argv.mode === 'development') {
    config.devtool = 'source-map';
  }

  if (argv.mode === 'production') {
    //...
  }

  return config;
};

export default configFactory;

@alexander-akait
Copy link
Member

I see, make sense... we need think how better to do it, maybe in future we will have more args...

@MarkKoester
Copy link

It seems clunky to put the responsibility on consumers to define the types they're concerned with, especially since the process for it is mostly just boilerplate (looking up the type from the configuration object). If webpack (CLI?) knows what properties it expects in as CLI arguments shouldn't those be exposed to consumers? Even if it's just something as simple as:

type Args = { [k in keyof Configuration]: string }

I'd rather make type safety the default case rather than having it rely on consumers correctly defining the configuration function type.

@alexander-akait
Copy link
Member

So only ConfigurationFactory left?

And

A type which I've noticed is still not exported is SourceMap. I've had to inline it in my code.

Should be easy right now, also we can solve this #12294

@vitoyucepi
Copy link

@alexander-akait I've tried to implement ConfigurationFactory and MultiConfigurationFactory in #11964.
But I know practically nothing about internal structure of webpack.

@alexander-akait
Copy link
Member

@vitoyucepi Bad solution, you need add js docs types, no need to duplicate CliConfigOptions, it is returned value from https://github.com/webpack/webpack/blob/master/lib/cli.js#L614, we need do it generic and then use for ConfigurationFactory

@AviVahl
Copy link
Author

AviVahl commented May 14, 2021

To be honest, it feels like the built-in types are in a much better shape now than when this issue was first opened.
I don't mind closing it to allow for a more organized discussion to happen in new issues (for any leftover issues; per type).
WDYT @alexander-akait?

@alexander-akait
Copy link
Member

@AviVahl Let's keep open until we solve ConfigurationFactory/MultiConfigurationFactory and then close in favor single issues

@harelmo
Copy link

harelmo commented Aug 26, 2021

Hi, in one of our TS projects, we have a custom ResolvePlugin implementation - and since the ResolvePluginInstance is not currently exported, we cannot make this compile without some TS compiler exclusions.
Any objections for exporting the ResolvePluginInstance type for public use?

@alexander-akait
Copy link
Member

@harelmo feel free to send a PR

@OnkelTem
Copy link

OnkelTem commented Oct 19, 2021

Just to recap on the ConfigurationFactory.

So the easiest way to get it typed now is to add types from #11630 (comment) :

import type { Configuration } from 'webpack';

export default (env: Record<string, any>, args: Record<string, any>): Configuration => {
  return { mode: 'production' }
}

And the current plan is to create an appropriate generic in webpack and to make it more specific in webpack-cli. Right?

@vitoyucepi
Copy link

@OnkelTem personally I think there are 4 variants to do it

  1. Your code snippet.
  2. Use unknown instead of any.
  3. Define env as env: string | Record<string, string | boolean | number> | unknown.
    Maybe this can look like env: Record<string, string>, or Dict<string> like in node ProcessEnv.
  4. Dump all args from webpack-cli. And refer them as CliConfigOptions.

@alexander-akait
Copy link
Member

Hello, There has been no activity here for a long time, so I close this issue, anyway it doesn't mean we don't want to provide more types, ping me here (and I will reopen and update the issue) or create a new issue if you want more types, thank you

@astorije
Copy link

@alexander-akait would you mind reopening this? ConfigurationFactory would still be very helpful!

@alexander-akait
Copy link
Member

We don't have ConfigurationFactory type, you can write it manually or take it from webpack-cli, because webpack doesn't have factory for configurations

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests