Skip to content

Resolving dependency conflicts when importing node package typings #4665

Closed
@mhegazy

Description

@mhegazy

This issue is explained in details in #2839

Terminology

Through out this issue, i will be referring to different ways of declaring external modules, here are what i mean by them.

Ambient external module declaration

Module declarations of the form:

declare module "mod" {
    ....
}

Proper external modules

The other way of writing a declaration of a module, i.e. a declaration file with a top level import or export; e.g.:

import { Contract } from ".\core"
export var MyContract: Contract;

Script file

A file that is not a module, i.e. does not include a top level import or export. it could however, include ambient external module declarations as described above.

Background

With module resolution work done in #4154 and #4352, it is possible for node package authors to distribute typings with their packages. For these packages, users do not have to independently acquire the typings from definitely typed, but will just import the package, the the compiler will locate the typings in the package directory (either index.d.ts or by following typings property in package.json in the package directory).

As noted in #2839, for a package author interested in distributing their typings with their package, there are dependencies that they need typings for; the most common would be node.d.ts, which is possibly referenced by each package.

The way they dependency typing are authored today on definitely typed is using ambient external module declaration; this indicates that these typings exist in the global scope, and thus are susceptible to conflicts. The classic the diamond dependency issue (courtesy of @poelstra):

  • myprogram
    • mylib
      • myutils@1.0
    • myotherlib
      • myutils@2.0

Now, if myUtils.d.ts uses ambient external module declaration, this is guaranteed to result in re-declaration errors.

Proposal

When resolving an import target in a node module, it is an error to include files that are not either:

  1. Proper external modules, or
  2. A Versioned script file - see below

For proper external modules, there are no conflicts expected, as external modules do no polute the global namespace.

For versioned files, referring to the same library, the compiler will follow a conflict resolution logic picking only the latest copy (as described by @basarat in #2839 (comment)). The files provided on the command line will be allowed to override this conflict resolution policy.

Versioned files

These are files declaring an identity, i.e. a name and a version. A versioned file is expected to contain a comment at the top of the form

/// <library name="myLibraryName" version="1.0.23.2-beta" />

Where the name field is optional, and if committed, the file name is used instead, and the version field follows the version definition in http://semver.org/

Metadata

Metadata

Assignees

No one assigned

    Labels

    In DiscussionNot yet reached consensusSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions