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

Type checking Javascript object literals is not open-ended in Maps #15618

Closed
ccprog opened this issue May 5, 2017 · 4 comments
Closed

Type checking Javascript object literals is not open-ended in Maps #15618

ccprog opened this issue May 5, 2017 · 4 comments
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@ccprog
Copy link

ccprog commented May 5, 2017

Seen in VS Code 1.12.1 == TypeScript 2.3

In a Javascript context, type-checking with @ts-check should treat objects as open maps, according to the wiki page. This does not work if a Map is declared with member objects that have varying properties:

// @ts-check

const map = new Map([
    ['a', {x: 1}],
    ['b', {y: 2}]
]);

const arr = [
    ['a', {x: 1}],
    ['b', {y: 2}]
];
const map2 = new Map(arr);

The y: 2 in line 5 raises this error message:

The type argument for type parameter 'V' cannot be inferred from the usage. Consider specifying the type arguments explicitly.
Type argument candidate '{ x: number; }' is not a valid type argument because it is not a supertype of candidate '{ y: number; }'.
Object literal may only specify known properties, and 'y' does not exist in type '{ x: number; }'.

The arr in line 12 raises a slightly different message:

Argument of type '((string | { [x: string]: any; x: number; })[] | (string | { [x: string]: any; y: number; })[])[]' is not assignable to parameter of type '[any, any][]'.
Type '(string | { [x: string]: any; x: number; })[] | (string | { [x: string]: any; y: number; })[]' is not assignable to type '[any, any]'.
Type '(string | { [x: string]: any; x: number; })[]' is not assignable to type '[any, any]'.
Property '0' is missing in type '(string | { [x: string]: any; x: number; })[]'.

It might be arguable whether this is a type checking bug, or whether the wiki documentation is misleading, as the JS relaxation of checking behaviour should only provide for late-adding members.

@mhegazy
Copy link
Contributor

mhegazy commented May 5, 2017

This is not related to the objects being open ended. the problem here is Map constructor is defined to take two type parameters, that are inferred from the call. the compiler tries to infer the type and comes up with two suggestions, {x: number} and {y: number}, none of them seem to subsume the other, in terms of properties, so it does not know how to handle these conflicts, and defers to the user to specify the type.

in a .ts file, you would have specified that as an explicit type parameter as new Map<{x?:number, y?:number}>(..) but that is not possible in a .js file. We need to figure out a solution for that.

For the second error, you can work around that by giving arr a type using JSDoc, as:

/** @type {Array<any>} */
const arr = [...];

@mhegazy mhegazy added the In Discussion Not yet reached consensus label May 5, 2017
@ccprog
Copy link
Author

ccprog commented May 6, 2017

That workaround would relax the type checking pretty far, since what I am aining for is

/** @type {Map.<string, {[x:string]: number}>} */

The best currently working solution, in terms of readability, seems to be

/** @type {Map.<string, {[x:string]: number}>} */
const map = new Map()
    .set('a', {x: 1})
    .set('b', {y: 2});

@mhegazy mhegazy added the Bug A bug in TypeScript label May 10, 2017
@mhegazy mhegazy added this to the TypeScript 2.4 milestone May 10, 2017
@mhegazy
Copy link
Contributor

mhegazy commented May 10, 2017

Based on discussion in #15747, We would like to:

  1. allow a new prefix cast syntax using /** @type {...} */ , e.g.:
// @ts-check

const map = new Map(/* @type {Array<[string, {x?:number, y?:number}]>} */[
    ['a', {x: 1}],
    ['b', {y: 2}]
]);
  1. flow contextual type from jsdoc @type annotations so that this is not an error:
// @ts-check

/** @type {Array<[string, {x?:number, y?:number}]>} */
const arr = [
    ['a', {x: 1}],
    ['b', {y: 2}]
];

@rbuckton
Copy link
Member

rbuckton commented Jun 8, 2017

I've split the suggestion for a cast syntax above into #16345.

@mhegazy mhegazy added the Fixed A PR has been merged for this issue label Jun 8, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests

3 participants