Skip to content

Provide a guide to refactor existing typings to be TS2 friendly #13294

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
cervengoc opened this issue Jan 5, 2017 · 7 comments
Closed

Provide a guide to refactor existing typings to be TS2 friendly #13294

cervengoc opened this issue Jan 5, 2017 · 7 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@cervengoc
Copy link

In many cases the typings for existing libraries are written somethings like this.

interface Something {
  ...
} 

interface MyPluginStatic {
  someThing: Somethings;
  ...
  // many more variables and functions
}

declare var myPluginGlobal: MyPluginStatic;

declare module "myplugin" {
  export = myPluginGlobal;
}

I have at least two examples, knockout and jquery.

In this form while I can import it like import myPluginVar from "myplugin" and then use it like myPluginVar.someThings, my problem is that I cannot augment this as an external module, and I have to still use the global augmentation. Technically it's fine, but it doesn't look very nice, also I could have naming collisions, etc.

On many forums one can read a lot about these libraries not providing a "modern" typings file, however, I haven't found anything about how they should look like then yet.

Please provide the equivalent format of the above mentioned sample typings file. Assuming that the library implementation supports UMD (like in most cases), the result typings file should be consumable with all kinds of imports, and it should support module level augmentation.

@mhegazy
Copy link
Contributor

mhegazy commented Jan 5, 2017

To be clear, these declaration files are valid in TS 2.0 as it was before.

This pattern has some short comings, which is that it puts all declarations in the global scope. this sometimes is desirable e.g. node definition files in @types/node has to be written this way, similarly any environment declarations like that for electron, or mocha.

For most of other modules, however, putting declarations in the global scope is not desired, and does not model reality. This model was used to enable UMD module consumption (i.e. using it as a global $ or using it through a module loader require("jquery")).

Starting with TS 2.0 there is a way to declare UMD modules that does not require modules to be added to the global scope.

So to answer your question, we recommend the declaration file be changed to a module using the UMD pattern.

You can find more documentation about the UMD pattern in http://www.typescriptlang.org/docs/handbook/declaration-files/library-structures.html#umd. You can find a template that we recommend using in module.d.ts.

One note about plugins, you can not change a plugin to use module augmentation untill you have updated the definition it augments.

As a quick example, for jquery, instead of:

declare var $: JqueryStatic;
interface JqueryStatic {
     ajax(settings: JQueryAjaxSettings): JQueryXHR;
     (): JQuery;
}
declare module "jquery" {
    export = $;
}

you would make it:

declare function jquery(): JQuery;
declare namespace jquery {
     export function ajax(settings: JQueryAjaxSettings): JQueryXHR;
}

export = jquery;
export as namespae $;

You can find a similar example in a PR i submitted a while back for angular 1, specifically this commit.

Also we have a set of checks that should be help you get to the recommended setup on definitllytyped. all you need is to add a tslint.json and run the lint task, this should give you errors as you go.

@mhegazy mhegazy added the Question An issue which isn't directly actionable in code label Jan 5, 2017
@DanielRosenwasser
Copy link
Member

Related StackOverflow question: http://stackoverflow.com/questions/41474704/how-could-i-update-the-knockout-typing-file-to-be-typescript-2-0-friendly

The problem with updating the Knockout type declarations is that there's a long string of globally-written declaration files for things like JQuery and the like that somehow depend on them. I suspect that's why it hasn't been rewritten.

@cervengoc
Copy link
Author

@mhegazy thank you for the thorough answer, it shed some serious light on things at me. For example I wasn't aware of the possibility to declare several symbols with the same name but different types, and export them all at once.

However, I still have some problems. I started to experiment with your given example, I made something like this.

declare function jquery(): JQuery;
declare namespace jquery {
     export function ajax(settings: JQueryAjaxSettings): JQueryXHR;
     ...
     export interface JQueryAjaxSettings {
       ...
     }
}

export = jquery;
export as namespae $;

Then I imported like this (using allowSyntheticalDefaultImports).

import jq from "jquery";

It's all fine, but when I reference somewhere jq.JQueryAjaxSettings, I get a compile error saying TS2694: Namespace 'jquery' has no exported member 'JQueryAjaxSettings'. I'm using Typescript 2.1.4, with the following tsproject.json.

{
  "compileOnSave": false,
  "compilerOptions": {
    ...
    "module": "amd",
    "moduleResolution": "node",
    "target": "es5",
    "declaration": true,
    "allowSyntheticDefaultImports": true,
    "outDir": "../build"
  },
  "exclude": [
    "../node_modules" 
  ]
}

What can cause this issue?

@aluanhaddad
Copy link
Contributor

Works in 2.1.4 and in latest. I don't get an errors. You will see that error however, if you are missing the export = clause and the file (incorrectly) contains other exports.

@cervengoc
Copy link
Author

It turned out that I had two different versions installed and I was trying to compile with 2.0.x, probably that caused the issue. After fixing it, I can now compile it too, thank you very much for the support. Now I'll work on refactoring linqjs and knockout declaration files to use this UMD pattern.

@cervengoc
Copy link
Author

Sorry to reopen this, but it seems like I'm still unable to augment a module which is in the above mentioned form.

I mean for example this doesn't seem to work.

declare module "jquery" {
  interface JQuery {
    myPlugin(): JQuery;
  }
}

I get a compile error that myPlugin is not found. Am I doing something wrong here?

@cervengoc cervengoc reopened this Jan 9, 2017
@cervengoc
Copy link
Author

Ok, it turned out that this augmentation fails only when I'm trying to consume the global $ varibale, and not importing the jquery module. When I import it with an import * as $ from "jquery" then the augmentation works.

However, I wonder how can one augment an UMD module declaration (which uses export as namespace) when it is consumed in the global scope (using the namespace it exported)?

@mhegazy mhegazy closed this as completed Feb 28, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

4 participants