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

JSON Module Type Resolution With Heterogeneous Arrays #37831

Closed
jcgsville opened this issue Apr 7, 2020 · 3 comments
Closed

JSON Module Type Resolution With Heterogeneous Arrays #37831

jcgsville opened this issue Apr 7, 2020 · 3 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@jcgsville
Copy link

jcgsville commented Apr 7, 2020

TypeScript Version: 3.9.0-dev.20200407

Search Terms:
json module array type resolveJsonModule

Code

schema1.json

[
  {
    "foo": 1,
    "bar": 2
  },
  {
    "baz": "something"
  }
]

schema2.json

[
  1,
  "foo"
]
import schema1 from './schema1.json';
import schema2 from './schema2.json';
const example1: [ { foo: number; bar: number }, { baz: string; } ] = schema1;
const example2: [ number, string ] = schema2;

Expected behavior:
I would expect this to compile correctly.

Actual behavior:
There are two compile errors on assignment.

Type '({ foo: number; bar: number; baz?: undefined; } | { baz: string; foo?: undefined; bar?: undefined; })[]' is missing the following properties from type '[{ foo: number; bar: number; }, { baz: string; }]': 0, 1

and

Type '(string | number)[]' is missing the following properties from type '[number, string]': 0, 1

Playground Link:
Because this relies on the resolveJsonModule flag, I cannot reproduce this in a typescript playground. Instead, I have created this small repository which demonstrates the failed compilation.
https://github.com/jcgsville/typescript-json-bug-repro

Related Issues:
Here is a similar issue, but doesn't address how it infers the type of arrays.
#27913

Discussion:
I could see an argument that this is intended behavior and a known limitation of the degree of sophistication of a type inferred from JSON. However, I'd expect that the JSON module resolution would resolve it to a more strict type, and the user of the module could loosen it on assignment/usage if they want.

For example, if the type of schema1 was resolved to be [ { foo: number; bar: number }, { baz: string } ], then assigning it to a looser type would succeed. See the following example

const schema3: [ { foo: number; bar: number }, { baz: string; } ] = [
  {
    foo: 1,
    bar: 2
  },
  {
    baz: 'something'
  }
];
const example3: ({ foo: number; bar: number; } | { baz: string })[] = schema3;

There are many use cases for heterogeneous arrays, but I encountered this when trying to represent anyOf in JSON Schema in a JSON module in a typescript project, which is currently impossible to do with @types/json-schema without an as unknown cast.

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Jun 1, 2020
@RyanCavanaugh
Copy link
Member

This is the intentional behavior because the "not specific enough" failure mode is fairly understandable, whereas the alternative where you get some enormous type full of literals because you imported a large JSON file is just unworkable. For example, I have seen projects where people import a 2 MB JSON file for type information -- turning that into the equivalent of an as const would tank performance and yield types you simply couldn't fit on any screen.

@jcgsville
Copy link
Author

That's a good point @RyanCavanaugh, and it's one I hadn't considered.

Do you think that there could be a future where TS could support both? Maybe you could represent the desire for Tuples (and maybe const objects as well?) with something like this

import schema2 from './schema2.json' as const;

@danvk
Copy link
Contributor

danvk commented Feb 11, 2021

@jcgsville I was going to propose the same as const qualifier on an import. But if you're going to make it as anything, you may as well specify the type you want:

type MyType = [ { foo: number; bar: number }, { baz: string; } ];
import schema2 from './schema2.json' as MyType;

ECMAScript import assertions are stage 3 already and feel related to this:

import config from './data/config.json' assert { type: 'json' };

I wonder if there's some nice way to co-opt that syntax to get the TypeScript type you want, e.g.

import config from './data/config.json' assert { type: 'json' } as MyType;

See #40694

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

3 participants