相关文章推荐
高大的凉茶  ·  一手遮天 Android - ...·  2 年前    · 
暴走的橡皮擦  ·  selenium.common.except ...·  2 年前    · 
打盹的灭火器  ·  laravel wherehas用法-掘金·  2 年前    · 
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I'm trying to write a lib in Typescript that I want to use in various other typescript or JS projects. All of the those other projects run in browsers.

My Typescript lib project has multiple files, and each file is a React component with 1 class and 1-2 interfaces (Component + Props + State, for those that are familiar with React).

I want my component library to be importable as an AMD module by the other projects, and I want all of the compiled JS code to be bundled in a single file as part of the library's build process.

So far I've managed to set everything up so that my lib compiles. I've set up the compiler with the following options : jsx: "react", declaration: true, module: "amd"

The generated code for the lib looks like:

define("epb-widget/WidgetItemNav", ["require", "exports", "react"], function (require, exports, React) {
    "use strict";
    exports.WidgetItemNav = ...;
define("epb-widget/WidgetItemInfo", ["require", "exports", "react"], function (require, exports, React) {
    "use strict";
    exports.WidgetItemInfo = ...;
define("epb-widget/WidgetItem", ["require", "exports", "react", "epb-widget/WidgetItemNav", "epb-widget/WidgetItemInfo"], function (require, exports, React, WidgetItemNav_1, WidgetItemInfo_1) {
    "use strict";
    exports.WidgetItem = ...;
define("epb-widget/WidgetOptions", ["require", "exports", "react"], function (require, exports, React) {
    "use strict";
    exports.WidgetOptions = ...;
define("epb-widget/Widget", ["require", "exports", "react", "epb-widget/WidgetItem", "epb-widget/WidgetOptions"], function (require, exports, React, WidgetItem_1, WidgetOptions_1) {
    "use strict";
    exports.Widget = ...;
define("epb-widget", ["require", "exports", "epb-widget/Widget", "epb-widget/WidgetItem", "epb-widget/WidgetItemInfo", "epb-widget/WidgetItemNav", "epb-widget/WidgetOptions"], function (require, exports, Widget_1, WidgetItem_2, WidgetItemInfo_2, WidgetItemNav_2, WidgetOptions_2) {
    "use strict";
    exports.Widget = Widget_1.Widget;
    exports.WidgetItem = WidgetItem_2.WidgetItem;
    exports.WidgetItemInfo = WidgetItemInfo_2.WidgetItemInfo;
    exports.WidgetItemNav = WidgetItemNav_2.WidgetItemNav;
    exports.WidgetOptions = WidgetOptions_2.WidgetOptions;

That's a lot of AMD modules for my taste, but I can live with it. The generated code looks like it would work well.

The generated .d.ts file looks like that:

declare module "epb-widget/WidgetItemNav" {
    import * as React from "react";
    export interface WidgetItemNavProps {
    export class WidgetItemNav extends React.Component<WidgetItemNavProps, void> {
declare module "epb-widget/WidgetItemInfo" {
    export interface WidgetItemInfoProps {
    export const WidgetItemInfo: (props: WidgetItemInfoProps) => JSX.Element;
declare module "epb-widget/WidgetItem" {
    export interface WidgetItemProps {
    export const WidgetItem: (props: WidgetItemProps) => JSX.Element;
declare module "epb-widget/WidgetOptions" {
    import * as React from "react";
    export interface WidgetOptionsProps {
    export interface WidgetOptionsState {
    export class WidgetOptions extends React.Component<WidgetOptionsProps, WidgetOptionsState> {
declare module "epb-widget/Widget" {
    import * as React from "react";
    import { WidgetItemProps } from "epb-widget/WidgetItem";
    export interface WidgetProps {
    export interface WidgetState {
    export class Widget extends React.Component<WidgetProps, WidgetState> {
declare module "epb-widget" {
    export { Widget, WidgetProps } from "epb-widget/Widget";
    export { WidgetItem, WidgetItemProps } from "epb-widget/WidgetItem";
    export { WidgetItemInfo, WidgetItemInfoProps } from "epb-widget/WidgetItemInfo";
    export { WidgetItemNav, WidgetItemNavProps } from "epb-widget/WidgetItemNav";
    export { WidgetOptions, WidgetOptionsProps } from "epb-widget/WidgetOptions";

Again, that looks quite reasonable. And that was all output directly by the Typescript compiler.

Finally, this library is an npm module, and its package.json looks like this

"name": "epb-widget", "version": "1.0.0", "description": "...", "main": "./dist/index.js", "typings": "./dist/index", "globalDependencies": {...}, "devDependencies": {...}

But here's the problem

When I try to use the library in one of my projects, it doesn't actually work.

I simply try import the classes like this:

import {Widget, WidgetProps, WidgetItem} from "epb-widget";

but the typescript compiler throws the following error

 error TS2656: Exported external package typings file '.../node_modules/epb-widget/dist/index.d.ts' is not a module. Please contact the package author to update the package definition.

I honestly can't figure out what I should do here. index.d.ts was generated by tsc, and I'm not sure why that very same tsc isn't able to consume it in another project.

The generated .d.ts looks like external module definitions, which is not what is expected for definitions files pointed to by the 'typings' parameter. I do this successfully with 'commonjs'; Is it an 'amd' issue ? What does you generated .d.ts look like if you choose 'commonjs' ? – Bruno Grieder Sep 1, 2016 at 16:09 Yes. I would think so. In the dependent projects, try using the generated .d.ts as an external typings files as you would do with a "pure" js library – Bruno Grieder Sep 1, 2016 at 16:47 Yup, it works. If you want to turn your comment into an actual answer, I'll upvote it and accept it. Thanks for the help. – LordOfThePigs Sep 2, 2016 at 7:54 For reference, do you mind pointing out where in the docs it mentions that there is a difference between the type definitions loaded via the typings folder in project vs the ones loaded via the typings property of package.json. I really didn't see it anywhere. – LordOfThePigs Sep 2, 2016 at 8:06

This is too long to go into our discussion above but here is my TL;DR to create a library:

  • create an index.ts that re-exports the definitions exposed in the library e.g.

    export * from './API'
    export * from './Decorators'
    
  • compile the library files with the --declaration flag to automatically generate the typings

  • add a typings entry to package.json pointing to the generated index.d.ts file
  • To use the library:

  • drop it in the node_modules folder like any other library
  • simply import * as mylib from 'mylib': it will automatically import all the exported definitions and the typings
  • This process works perfectly fine with commonjs and from what I understand, with systemjs too. I do not know about amd, and your generated typings look quite different from mine:

    The generated index.d.ts typically looks like

    export * from './API';
    export * from './Decorators';
    

    and the API.d.ts file will typically look like

    import * as stream from 'stream';
    import * as net from 'net';
    import * as url from 'url';
    export interface Factory {
        end(): Promise<void>;
    export interface ChannelFactory extends Factory {
        getChannel(name: string, closeListener?: () => void): Promise<Channel>;
        existsChannel(name: string): Promise<boolean>;
                    Apparently, the issue is that it works fine as longs as you DON'T combine the type declarations into a single file (ie: if you use --outDir it works, if you use --out it doesn't). From a few more tests I did today and from the links you provided, it seems that the best way to properly package everything is to do 2 passes on the TS code. 1st pass generates the JS code packed into one file. Second pass generates the type definitions as separate files. Then everybody is happy.
    – LordOfThePigs
                    Sep 2, 2016 at 9:35
                    5 years later and still relevant. I have generated lib/index.js with webpack and type definitions with TS. TS spits out separate .d.ts files, e.g. lib/src/Text.d.ts and lib/src/Title.d.ts. However, type definitions cannot be recognised if I use --outFile to produce single lib/index.d.ts combining both Text.d.ts and Title.d.ts. Have anyone managed to solve this?
    – igor
                    Feb 9, 2021 at 21:32
                    Hey @igor, same here. And I need to use --outFile since it's a Bazel target and it needs to be a single file. Could you solve it yet?
    – bazaglia
                    Jan 3, 2022 at 20:41
            

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.

  •