Skip to content

Errors in generated declaration files #37552

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

Closed
ernestostifano opened this issue Mar 24, 2020 · 4 comments · Fixed by #39818
Closed

Errors in generated declaration files #37552

ernestostifano opened this issue Mar 24, 2020 · 4 comments · Fixed by #39818
Assignees
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue

Comments

@ernestostifano
Copy link

ernestostifano commented Mar 24, 2020

TypeScript Version: 3.9.0-dev.20200324

Search Terms: declaration errors, declaration files, TS1147, TS2307, TS2503

(ISSUE EXPLANATION BELOW)

Code

Source:

./file-a.js

/**
 * @namespace myTypes
 * @global
 * @type {Object<string,*>}
 */
const myTypes = {
    // SOME PROPS HERE
};

/** @typedef {string|RegExp|Array<string|RegExp>} myTypes.typeA */

/**
 * @typedef myTypes.typeB
 * @property {myTypes.typeA}    prop1 - Prop 1.
 * @property {string}           prop2 - Prop 2.
 */

/** @typedef {myTypes.typeB|Function} myTypes.typeC */

export {myTypes};

./file-b.js

import {myTypes} from './file-a.js';

/**
 * @namespace testFnTypes
 * @global
 * @type {Object<string,*>}
 */
const testFnTypes = {
    // SOME PROPS HERE
};

/** @typedef {boolean|myTypes.typeC} testFnTypes.input */

/**
 * @function testFn
 * @description A test function.
 * @param {testFnTypes.input} input - Input.
 * @returns {number|null} Result.
 */
function testFn(input) {
    if (typeof input === 'number') {
        return 2 * input;
    } else {
        return null;
    }
}

export {testFn, testFnTypes};

Generated Declarations:

./file-a.d.ts

/**
 * @namespace myTypes
 * @global
 * @type {Object<string,*>}
 */
export const myTypes: {
    [x: string]: any;
};
export namespace myTypes {
    export type typeA = string | RegExp | (string | RegExp)[];
    export type typeB = {
        /**
         * - Prop 1.
         */
        prop1: string | RegExp | (string | RegExp)[];
        /**
         * - Prop 2.
         */
        prop2: string;
    };
    export type typeC = Function | typeB;
}

./file-b.d.ts

/** @typedef {boolean|myTypes.typeC} testFnTypes.input */
/**
 * @function testFn
 * @description A test function.
 * @param {testFnTypes.input} input - Input.
 * @returns {number|null} Result.
 */
export function testFn(input: boolean | Function | myTypes.typeB): number; // TS2503 ERROR HERE!
/**
 * @namespace testFnTypes
 * @global
 * @type {Object<string,*>}
 */
export const testFnTypes: {
    [x: string]: any;
};
export namespace testFnTypes {
    export type input = boolean | Function | myTypes.typeB;
    import { myTypes } from "./file-a.js"; // TS1147, TS2307 ERRORS HERE!
}
// WHY IS ABOVE IMPORT STATEMENT NOT PLACED HERE? MANUALLY DOING IT, APPARENTLY SOLVES THE ISSUE.

Issue explanation:

TypeSctript does a very good job generating declaration files based on existing JS files, even in complex scenarios.

However, some errors appear when TypeScript decides to import declarations from other files. This happens when referenced declarations are sufficiently large (as in this example) to not be just copied.

In the ./file-b.d.ts generated declaration file I get the following errors in order of appearance (see code):

  • TS2503: Cannot find namespace 'myTypes'.
  • TS1147: Import declarations in a namespace cannot reference a module.
  • TS2307: Cannot find module './file-a.js' or its corresponding type declarations.

When the import statement is moved outside of the namespace, all errors disappear (makes sense from a JS point of view).

Beside this highlighted errors, only seen when opening the generated declaration files. Everything seems to work as intended (Type checking, auto-completion, etc.). Even when consuming the compiled library with the generated declaration files included (with those "errors" present).

Expected behavior:

No error to be present in generated declaration files.

Actual behavior:

Errors are present as explained above.

Playground Link: Not possible because import/export involved.

Related Issues: None.

Additional Details:

TypeScript Configuration
{
    "compilerOptions": {
        "target": "ESNext",
        "module": "ESNext",
        "moduleResolution": "Node",
        "lib": ["ESNext", "DOM"],
        "allowJs": true,
        "checkJs": true,
        "strict": true,
        "noImplicitAny": false,
        "strictNullChecks": false,
        "types": ["node", "mocha", "chai"],
        "esModuleInterop": false,
        "forceConsistentCasingInFileNames": true,
        "useDefineForClassFields": true
        "noEmit": false,
        "noEmitOnError": false,
        "declaration": true,
        "emitDeclarationOnly": true,
        "declarationDir": "./types"
    },
    "files": ["./src/index.js"],
    "exclude": ["./src/__tests__/**/*.js"]
}
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 4.0 milestone Mar 24, 2020
@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Mar 24, 2020
@ernestostifano
Copy link
Author

@RyanCavanaugh @weswigham I've run additional tests and it seems that this issue is causing problems when consuming the generated declaration files. Specifically, typescript won't compile a project if a package with the above faulty declarations is being used.

I would suggest to rise priority for this bug. TypeScript 4 might be too far in the future.

@ernestostifano
Copy link
Author

ernestostifano commented Mar 24, 2020

In some scenarios, when there is a name conflict, moving the import statement out of the namespace might not be enough. Maybe import with alias should be used in this cases.

It is strange, because I remember seeing the import with alias strategy in some generated declaration files in the past. It seems that the generation logic is not consistent.

Example:

export namespace myNamespece {
    export namespace nestedNamespace {
        export type someType = string;
    }
    export namespace nameConflict {
        export type anotherType = {
            typeA?: boolean;
            typeB?: nameConflict.externalType;
        };
        import { nameConflict } from "./path/to/type.js"; // TS1147, TS2307 ERRORS HERE!
    }
}

@ernestostifano
Copy link
Author

@RyanCavanaugh @weswigham sorry to bother you again. If you point me to the section in your codebase where this logic is defined, I would be glad to fix it myself. Please let me know.

@weswigham
Copy link
Member

In checker.ts, the body of serializeSymbolAsFunctionNamespaceMerge or thereabouts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue
Projects
None yet
4 participants