microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.7k stars 12.45k forks source link

Improve Declaration File Acquisition #9184

Closed DanielRosenwasser closed 8 years ago

DanielRosenwasser commented 8 years ago

In TypeScript 2.0, we want to make acquiring declaration files easier. We'd like to consolidate .d.ts management with general dependency management through npm.

Background

TypeScript uses declaration files (files ending in .d.ts, also called "definition" files) to describe the shape and functionality of other libraries. Usually, new declaration files are submitted to DefinitelyTyped, a community-driven repository of declaration files. Unfortunately, there are issues with the way a definition file gets from there to your project today.

Once a declaration file is on DefinitelyTyped, you can get it through tools like Typings or tsd - package managers for declaration files. While very useful, these were extra tools to learn, and this added friction for new users. There were also some technical issues, such as lack of versioning and conflicting declarations, that existing tools couldn't manage.

The new world

So let's jump to the new world to contrast against the current one. Grabbing a declaration file won't require using tsd or Typings at all. It's just an npm command away:

npm install --save @types/lodash

That command does two things:

  1. Grabs the declaration files for lodash and saves it to a directory named @types/lodash in our package's node_modules.
  2. Saves that as a dependency in our package.json.

@types/lodash is nothing more than a scoped package. We have taken the time to import files from DefinitelyTyped into their own scoped package. But why does this matter, and why is it any different from how things worked before?

Well in TypeScript 2.0, this @types folder is going to be significant. Usually when we try to try to import "lodash", we look in ./node_modules/lodash, ../node_modules/lodash, ../../node_modules/lodash, etc. for type declarations. Instead, each time we peek into a node_modules folder as we climb up, we'll look for @types/lodash first, and then for lodash.

That might sound surprising, but the logic is that if the lodash package itself had type declarations, you wouldn't have installed @types/lodash.

Global declaration files

Some declaration files only affect the globals, and don't use modules (e.g. Jasmine). In most cases, TypeScript will automatically pick them up from the typeRoots option. By default, typeRoots will just be node_modules/@types/ and node_modules/, relative to a tsconfig.json or for loose files, the files themselves.

If for some reason, you need to include Jasmine but it's not present in your typeRoots, then you can use the types compiler option to trigger the same sort of resolution that happens for modules, where TypeScript will look in ./node_modules/@types/, ./node_modules, and keep climbing up.

The types option could be used something like this.

{
    "compilerOptions": {
        "types": ["jasmine"]
    }
}

Going forward

Library authors will be getting attention in 2.0 as well. We're also introducing new features that simplify the way .d.ts files can be authored for libraries that are both modules and globally defined (also called universal modules). Apart from that, everything stays the same - for instance, new declarations and fixes should still go to DefinitelyTyped. In the future, we'll go into this in more detail and have some prescriptive documentation.

Acknowledgements

We also owe a great thanks to those who dedicated their own time into efforts like Typings and tsd. These tools helped bring TypeScript where it is today and ultimately helped guide us on the direction for this effort. Specifically, Blake Embrey, the maintainer and creator of Typings, has worked closely with us during this entire process and given us extremely valuable feedback. In addition to this, Diullei Gomes and Bart van der Schoor, maintainers of tsd, have helped lay the foundation from the beginning.

Finally, the entire DefinitelyTyped community - a group that has grown to over 2000 contributors at this point - have demonstrated, and continue to show, the kind of enthusiasm and support outside of the core team. We're grateful for all the great work that's been done here.

aluanhaddad commented 8 years ago

@ai I follow you. I wasn't thinking about the specific nature of your library when I made my comment. However I think your idea is a wonderful idea. This would benefit a number of other scenarios including other package managers and generally decouple typescript from npm.

jednano commented 8 years ago

That's definitely a good idea, so long as versioning is accounted for. I suppose the URL would have to change every time the declaration files are updated to a newer version.

aluanhaddad commented 8 years ago

I think that your own should point to a registry and have an embedded semver tag. That would redirect to the actual definition.

jednano commented 8 years ago

The only thing that's odd is that an IDE would automatically install something at startup. I'm not sure that's a normal IDE responsibility. Maybe it would be best to prompt the user to install or ignore, but that's an implementation detail.

aluanhaddad commented 8 years ago

I'd consider that normal IDE Behavior like restoring packages or downloading extension manifests, I don't see that as an issue at all.

remojansen commented 8 years ago

Packages in the @types scope are auto generated from definitions on https://github.com/DefinitelyTyped/DefinitelyTyped using https://github.com/Microsoft/types-publisher.

If a PR gets merged into DefinitelyTyped how long does it take for the types-publisher to run an update the npm module? When is it executed?

mhegazy commented 8 years ago

If a PR gets merged into DefinitelyTyped how long does it take for the types-publisher to run an update the npm module? When is it executed?

Today, i have to run them manually when we need to publish. we are working on making it work on a web hook, hopefully by end of next week should be up and running. the publishing of a package, takes a few minutes. so once the web hook is active, it should be 5-10 mins between accepting a PR and publishing the package.

mhegazy commented 8 years ago

Declaration file authoring, and how to use @types packages can be found at https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/declaration%20files/Introduction.md

realityfilter commented 8 years ago

What is the correct procedure to report missing type definitions under @types that are already present in DefinitelyTyped? Like xpath for example?

mhegazy commented 8 years ago

What is the correct procedure to report missing type definitions under @types that are already present in DefinitelyTyped? Like xpath for example?

I would file an issue on https://github.com/DefinitelyTyped/DefinitelyTyped

yahiko00 commented 8 years ago

I'm currently testing TS 2.0 beta. And "npm-install" typings is a nice improvement, way faster than grabing definition files from DT.

huy-nguyen commented 8 years ago

@DanielRosenwasser You mentioned in the first post that type definitions in @types packages will be read first before definitions included inside a package (e.g. index.d.ts):

Well in TypeScript 2.0, this @types folder is going to be significant. Usually when we try to try to import "lodash", we look in ./node_modules/lodash, ../node_modules/lodash, ../../node_modules/lodash, etc. for type declarations. Instead, each time we peek into a node_modules folder as we climb up, we'll look for @types/lodash first, and then for lodash.

That might sound surprising, but the logic is that if the lodash package itself had type declarations, you wouldn't have installed @types/lodash.

However, when I compile one of my repos with tsc 2.0 and enable --traceResolution, this is what I see instead:

======== Resolving module 'redux' from '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/reducers/reducer.ts'. ========
Module resolution kind is not specified, using 'NodeJs'.
Loading module 'redux' from 'node_modules' folder.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/reducers/node_modules/redux.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/reducers/node_modules/redux.tsx' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/reducers/node_modules/redux.d.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/reducers/node_modules/redux/package.json' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/reducers/node_modules/redux/index.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/reducers/node_modules/redux/index.tsx' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/reducers/node_modules/redux/index.d.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/reducers/node_modules/@types/redux.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/reducers/node_modules/@types/redux.tsx' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/reducers/node_modules/@types/redux.d.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/reducers/node_modules/@types/redux/package.json' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/reducers/node_modules/@types/redux/index.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/reducers/node_modules/@types/redux/index.tsx' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/reducers/node_modules/@types/redux/index.d.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/node_modules/redux.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/node_modules/redux.tsx' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/node_modules/redux.d.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/node_modules/redux/package.json' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/node_modules/redux/index.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/node_modules/redux/index.tsx' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/node_modules/redux/index.d.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/node_modules/@types/redux.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/node_modules/@types/redux.tsx' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/node_modules/@types/redux.d.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/node_modules/@types/redux/package.json' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/node_modules/@types/redux/index.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/node_modules/@types/redux/index.tsx' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/ts/node_modules/@types/redux/index.d.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/node_modules/redux.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/node_modules/redux.tsx' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/node_modules/redux.d.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/node_modules/redux/package.json' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/node_modules/redux/index.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/node_modules/redux/index.tsx' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/node_modules/redux/index.d.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/node_modules/@types/redux.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/node_modules/@types/redux.tsx' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/node_modules/@types/redux.d.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/node_modules/@types/redux/package.json' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/node_modules/@types/redux/index.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/node_modules/@types/redux/index.tsx' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/src/node_modules/@types/redux/index.d.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/node_modules/redux.ts' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/node_modules/redux.tsx' does not exist.
File '/Users/huynguyen/Sites/restaurant-menu-builder/node_modules/redux.d.ts' does not exist.
Found 'package.json' at '/Users/huynguyen/Sites/restaurant-menu-builder/node_modules/redux/package.json'.
'package.json' has 'typings' field './index.d.ts' that references '/Users/huynguyen/Sites/restaurant-menu-builder/node_modules/redux/index.d.ts'.
File '/Users/huynguyen/Sites/restaurant-menu-builder/node_modules/redux/index.d.ts' exist - use it as a name resolution result.
Resolving real path for '/Users/huynguyen/Sites/restaurant-menu-builder/node_modules/redux/index.d.ts', result '/Users/huynguyen/Sites/restaurant-menu-builder/node_modules/redux/index.d.ts'
======== Module name 'redux' was successfully resolved to '/Users/huynguyen/Sites/restaurant-menu-builder/node_modules/redux/index.d.ts'. ========

It looks like the compiler is always searching for type definitions inside the npm package itself first before searching inside @types package. What do you think?

matijagrcic commented 8 years ago

This would get a lot more traction if the DefinitelyTyped readme file included

qc00 commented 8 years ago

@mhegazy I have the exact same problem as @realityfilter where a package (numeraljs) already exists in DefinitelyTyped for months but does not show up in NPM under @types.

The types-publisher documentation seems to mention some definitions.json file that seems to be manually maintained by you guys?

RyanCavanaugh commented 8 years ago

@billccn the package was published under the name https://www.npmjs.com/package/@types/numeral because that was the package name the declaration claimed to be for: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/numeraljs/numeraljs.d.ts#L45

RyanCavanaugh commented 8 years ago

Indeed that's the actual package name itself https://www.npmjs.com/package/numeral -- the rule is always that the types package name matches the NPM package name (if one is available)

mindplay-dk commented 8 years ago

Where is the documentation?

I saw this and that, but I don't see an example of package.json and tsconfig.json for a type-safe Typescript 2.0 module - an actual type-safe package; that's what this feature is about, right?

A working example of a correctly packaged module, correctly configured to build it's .d.ts file, and a package using that module, with type-safety, would be really useful.

blakeembrey commented 8 years ago

@mindplay-dk That's a separate feature, something that's been available since 1.5 of TypeScript. It uses the package.json -> typings field. You can find any TypeScript module under my user is published using this approach. The @types approach is about improving accessibility of third-party typings for non-TypeScript modules.

Edit: Here's a simple example - https://github.com/blakeembrey/free-style/blob/master/package.json#L6 which is used in https://github.com/blakeembrey/react-free-style/blob/master/package.json#L48. TypeScript resolves the field when using node module resolution.