Skip to content
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

JavaScript types in TypeScript #4790

Closed
billti opened this issue Sep 14, 2015 · 7 comments
Closed

JavaScript types in TypeScript #4790

billti opened this issue Sep 14, 2015 · 7 comments
Labels
Fixed A PR has been merged for this issue Suggestion An idea for TypeScript

Comments

@billti
Copy link
Member

billti commented Sep 14, 2015

JavaScript types in TypeScript

This write-up outlines considerations for potential integration of JavaScript and TypeScript at the type system level. (See parent issue #4789 for an overview)

Basic inference as it works today

Several constructs in JavaScript will be recognized and typed using TypeScript inference today, for example:

var x = 10;            // <- number
var y = [true, false]; // <- Array<boolean>
var z = {              // <- {a: string; b: Date}
  a: "test", 
  b: new Date()
}
function f(){          // <- { (): number; }
  return 3.14;
}

There are several limitations however that the TypeScript type annotations were designed to support. For example:

function foo(a, b){
  return a + b;
}

Here there is no way to infer the types of 'a' or 'b' (and thus also the return type), and no way in the JavaScript syntax to specify the parameter types. For simple cases, this case be accurately and equivelently modeled using JsDoc annotations. For example:

/**
 * @param {string} a
 * @param {number} b
 * @returns {string}
 */
function foo(a, b){
  return a + b;
}

Which is identical in meaning to the following TypeScript:

function foo(a: string, b: number): string {
  return a + b;
}

(Note that in this simple case the return type is not necessary, and could have been inferred once the paramater types were known).

Below shows some more JsDoc annotations that model similar TypeScript constructs closely, such as union types, object literal types, optional types, "any" types, generic types, rest args, referencing declared types, etc...

/** This function demonstrates union types, object literal types, "any" types, and optional params
 * @param {(number|string)} a - First param is string or number
 * @param {{name: string, age: number}} b - Second param is an object type
 * @param {*} [c] - Third param is any type, and optional
 * @return {Object}
 */
function myFunc(a,b,c){
    if (c && b.age > 42) return {};
}


/** This function demonstrates generic types and rest args
 * @template T
 * @param {T} x - Some input type
 * @param {...number} y - A list of numbers 
 * @return {T} - Return type will be the type of the first parameter
 */
function func2(x, y){
    return x;
}
func2("test", 1, 2, 3).charAt(2);


/** @type {Document} */
var someDoc;   // Equivalent to "var someDoc: Document" in TypeScript

Consuming and providing types

Similar to the way TypeScript works today, files are included in a project "context", and the values and types in that context are visible across files (according to the rules on scope and visibility). For example, the interface foo may be declared in file foo.d.ts, which may be included in the project context via the command-line, tsconfig.json, or a /// <reference ..> line at the start of the file. JsDoc annotations would then be able to reference the type foo in the JavaScript files.

Similarly, the JavaScript file may declare a value test with a type of {name: string, age: number}, and the identifier test would then be available to TypeScript files in the same context with the type given.

Beyond the simple scenarios for values and types given above, this also extends to the module system, where code authored in JavaScript may import a module written in TypeScript, and vice-versa.

TODOs and open questions:

  • Point to the current prototype and outline the JsDoc tags supported in it
  • Reference the analysis data on the JsDoc types seen most commonly in code analyzed
  • Determine the value or requirements for accurately modeling existing system, e.g.
    • Do we need/want to be compatible with Closure Compiler?
    • Do we want JavaScript type declarations to be equivalent in expressiveness to TypeScript?
  • Outline using common JavaScript class patterns (using both ES6 classes and adorning the 'prototype' property of a constructor function)
  • Decribe declaring interfaces (using @typedef)
  • Outline declaration and consumption of module types in JavaScript (CommonJS, AMD, and ES6)
  • Verify and describe usage of newer ES6 features such as generators, spread/rest args, optional/default params/members, etc...
  • Outline patterns the TypeScript engine doesn't support today for type inference from JavaScript code (e.g. mixin patterns, expando objects, code flow sensitive types, dynamic 'this' type, etc...)
  • Discuss integration concerns, e.g.
    • How do JavaScript types appear in TypeScript? Are they strongly typed only in explicitly annotated?
    • How do types flow between them (including round-tripping)?
    • How to allow for future iteration in a backwards compatible way (e.g. if inference or type flow changes)?
@Arnavion
Copy link
Contributor

If you do decide to be compatible with Closure Compiler's annotations, please also consider letting TS parse its nullability qualifiers (?number, !SomeType) etc, even if it just ignores them and treats them all as nullable for the purpose of type checking.

@yortus
Copy link
Contributor

yortus commented Jan 18, 2016

EDIT: Fixed in 1.8, can disregard the rest of this comment now....


Another case that might need consideration here is ES7 async/await:

/**
 * @return {CustomPromise}
 */
async function test(){
    return 42;
}

let result = test(); // native Promise or CustomPromise?

Running this in a JavaScript engine that supports async/await (eg in MS Edge) will assign an ES6 Promise instance to result, regardless of the type annotation (see #6068).

But what if this was run through the TypeScript compiler first, e.g. using --allowJs and --outFile? If tsc sees this as async function test(): CustomPromise { return 42; } it will emit code that actually returns a CustomPromise instance so producing a different runtime result.

Return type annotations on TypeScript async functions are not ambient type information - they actually change the emitted code. See also #6007.

@mhegazy mhegazy closed this as completed Feb 2, 2016
@mhegazy
Copy link
Contributor

mhegazy commented Feb 2, 2016

this is fixed in 1.8, see #6581

@mhegazy mhegazy added Fixed A PR has been merged for this issue and removed In Discussion Not yet reached consensus labels Feb 2, 2016
@mhegazy mhegazy added this to the TypeScript 1.8.2 milestone Feb 2, 2016
@ShimShamSam
Copy link

This may sound crazy, but one extra way to define the type of a variable would be using labels. The following is perfectly valid code and conveniently works with existing JS syntax highlighting:

String: var foo;
String: var bar;
Number: var baz;
Object: var biz;
CustomObject: var fee;

// TypeScript should throw errors for these
foo = 1;
bar = 2;
baz = 'asd';
biz = 'asd';
fee = [];

@ahamid
Copy link

ahamid commented May 21, 2016

Are there docs on this? I see this in the wiki but cannot figure out how to enable this. A js file included by virtue of allowJs does not appear to get typechecked, and the allowNonTsExtensions flag used in #6581 does not seem to be available to the tsc command line (or related tools, gulp-typescript...).
Edit: use of types defined in js file imported by ts file do appear to be typechecked; code in js file itself does not appear to be typechecked
Edit2: maybe this is being pursued under "Salsa"?

@mhegazy
Copy link
Contributor

mhegazy commented May 22, 2016

There are no errors reported in the js files at this time. Only consuming them; I.e. With --allowjs you can use types from a js file.
Next step is to give a customized error experience for js files. We have found that what errors users expect In a js file is rather subjective. We would like to make errors in js file configurable and allow users to opt in and out of them. But this is a non-trivial work item that is not on our commited list of work items at the moment.

Salsa is a loose term to refer to the TS compiler understanding, consuming, and providing language services in js files. I consider this largely implemented with possible enhancements in the future. Salsa is what derives the js IDE experience in VSCode today and some project contexts in VS as well (e.g. Node tools).

@aaronbeall
Copy link

There are no errors reported in the js files at this time. Only consuming them; I.e. With --allowjs you can use types from a js file.
Next step is to give a customized error experience for js files.

I have a large JS project and I was quite surprised by this. Of course I understand the challenges here but am I correct that at this time there's no way to get type check errors in a pure JS project? Is this on the roadmap?

@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
Fixed A PR has been merged for this issue Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

8 participants