"Flattened" type defintions
kitsonk opened this issue · 5 comments
Search Terms
lib generator flatten bundle types
Suggestion
Have a feature of the compiler that allow for emitting of all the exported types of a given module in a flattened way, so that a consumer of the types can have a single file that abstracts them away from the JavaScript implementation. Exported symbols and related types, and their dependencies would be "flattened" and exported in a single UMD .d.ts
file.
Currently, there are two supported ways of generating type definitions for a collection of modules. The most common is that you can emit the declaration files for the project, which will generate individual "UMD" .d.ts
files for each file in the implementation. This requires the developer to distribute all these individual files, which subsequently the TypeScript compiler has to consume. When a consumer goes to definition, they are not abstracted from the implementation, making it difficult to give a comprehensive understanding.
The other mechanism is to emit a declaration file with an outFile and choose a module format like SystemJS or AMD. This outputs a single .d.ts
file, but generates a number of namespaces and module declarations, and while functional, makes it more difficult for a consumer to understand the package.
Use Cases
Specifically in Deno, we have to generate our runtime type library, based upon the TypeScript code that identifies that runtime environment. We have a single module which defines the global runtime scope, imports all the other modules that define the runtime module and hoists that into the global scope. Previously, we utilised a TypeScript AST manipulation tool (ts-morph) to generate the runtime type library, but removed it when we removed Node.js as part of our build pipeline. We attempted to utilise the outFile
approach, but it generated a correct but non-sensical type library. While we wouldn't expect the full generation of the runtime type library, having a single .d.ts
file that contained all the exported symbols from the "main" module, plus re-writting all the other imported dependency types as a single flat file, which we can lightly modify to be the default library file for the Deno runtime.
In general, it is logical that other TypeScript library authors would like to distribute a single .d.ts
file that has readable output to distribute along with their library.
Examples
Given something like the following:
foo.ts
import * as bar from "./bar";
import * as baz from "./baz";
export const foo = {
bar: new Bar(),
baz: new Baz(),
};
export const qat = "qux";
bar.ts
export class Bar {
bar() {
return "bar";
}
}
baz.ts
export class Baz {
baz() {
return 1;
}
}
When the "flattened" declaration would be generated would look something like this:
class Bar {
bar(): string;
}
class Baz {
baz(): number;
}
export const foo: { bar: Bar, baz: Baz };
export qat: "qux";
I'm sure I haven't thought of all the edge cases, like when there is a conflict in names, and therefore how to namespace such dependencies.
Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
Is *.d.ts rollup
in https://www.npmjs.com/package/@microsoft/api-extractor that you wanted?
@plantain-00 not exactly, that is a rollup of .d.ts
files, it doesn't flatten.
@j-oliveras likely a duplicate of #4433, this came out of an in person conversation with @RyanCavanaugh. It could be we both forgot about #4433 but it would likely work. @weswigham do you still want to keep #4433 alive? If so I would be glad to let this one go.
@kitsonk I don't know what's your flatten
, the generated file by @microsoft/api-extractor
looks flattened to me:
declare class Bar {
bar(): string;
}
declare class Baz {
baz(): number;
}
export declare const foo: {
bar: Bar;
baz: Baz;
};
export declare const qat = "qux";
A known issue is "import * as ___ from ___;" is not supported yet for local files
, use import { Bar } from "./bar";
instead.
I think #4443 is what we were discussing. It'd be great to include your OP as a comment there for a strong motivating scenario