Typescript: import ConstJson from './config.json' as const;

Created on 24 Jun 2019  ·  16Comments  ·  Source: microsoft/TypeScript

Search Terms

json const assertion import

Suggestion

The ability to get const types from a json configuration file.

IE if the json is:

{
  appLocales: ["FR","BE"]
}

I want to import the json and get the type {appLocales: "FR" | "BE"} instead of string

Use Cases

Current approach gives a too broad type string. I understand it may make sense as a default, but having the possibility to import a narrower type would be helpful: it would permit me to avoid maintaining both a runtime locale list + a union type that contains the values that are already in the list, ensuring my type and my runtime values are in sync.

Checklist

My suggestion meets these guidelines:

  • [X] This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • [X] This wouldn't change the runtime behavior of existing JavaScript code
  • [X] This could be implemented without emitting different JS based on the types of the expressions
  • [X] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • [X] This feature would agree with the rest of TypeScript's Design Goals.

Links:

This feature has been mentionned:

Awaiting More Feedback Suggestion

Most helpful comment

I'm for option #3. This one looks similar to the current "as const" syntax:

const x = {...} as const

It makes it more intuitive. Definitely a killer feature for config-based code if Typescript adopts it.

All 16 comments

This would be extremely useful for adding direct support for JSON schemas to TypeScript. This can technically be accomplished with generators right now, but it would be so much more elegant to be able to use mapped types (for example, https://github.com/wix-incubator/as-typed) to map an imported JSON schema to its corresponding TypeScript type. It isn't currently possible to use this approach with a JSON import since the type property of each schema object will be a string instead of 'boolean' | 'string' | 'number' | ....

FWIW I just tried to do this and used the exact same syntax that the issue title uses, if that's any indication of how intuitive it is 😁

I just tried the exact above syntax also. Const assertion is a fantastic tool and it would be incredible to have the ability to assert static json files at import

I added a note to #26552 and now realize that I put it in the wrong place, so copying it over here :D

Reading JSON more literally into string types would be a significant improvement to be able to put configs into JSON.

As an example, the WordPress Gutenberg project is moving towards a JSON registration schema for multiple reasons. The list of available category options could and should be tightly limited to the available options. However, due to this bug, we could never enforce a proper category list which effectively breaks TS linting of the file for anyone wanting to use TS when creating Gutenberg blocks or plugins.

I've been trying to work on a fix for some of these issues here: https://github.com/pabra/json-literal-typer. If your use-case is relatively straightforward (limited special characters, no escape characters in string literals), then it may satisfy some needs. Would love to have this built-in to the language, but hopefully this will be helpful to some in the interim.

// CC: @DanielRosenwasser @RyanCavanaugh

How about syntax import const ConstJson from './config' and limited for the json modules.
I'm happy to work on this if it could be accept.

@Kingwl I think this syntax could be slightly more confusing than the alternatives. It could look similar to the import name when viewed in a sequence of imports. It would be good to get some a view on what the preferred syntax would be for everyone.

1 - import const myJson from './myJson.json';
2 - const import myJson from './myJson.json';
3 - import myJson from './myJson.json' as const';

Personal view:

1 - As mentioned above, not sure it's optimal due to distance from import name

2 - Perhaps too similar to const foo = 'abc'. I think this would at first pass look more like a variable assignment than an import

3 - This is more similar to the behaviour we have for as const so I would vote for this as the one that fits current design the best.

Thoughts? Have I missed any alternative syntax options?

Also happy to work on this if it progresses!

I'm for option #3. This one looks similar to the current "as const" syntax:

const x = {...} as const

It makes it more intuitive. Definitely a killer feature for config-based code if Typescript adopts it.

As great as this suggestion is, how should TypeScript interpret the type of property in the original comment?

{
  "appLocales": [ "FR", "BE" ]
}
  1. Sealed tuplet: readonly [ "FR", "BE" ]
  2. Tuplet: [ "FR", "BE" ]
  3. Array of custom strings: ("FR" | "BE")[]
  4. Array of arbitrary strings: string[] _← the current one_

I think, there's no way for TypeScript to know the desired level of strictness without developer explicitly specifying it somehow, — and it looks like this will have to be done per each file, rather than once in _tsconfig.json_

I think I'm gonna answer my own question 🙂

The const keyword in as const is not much about inferring the types literally, as it is about declaring the value as immutable, read-only, sealed. _That in turn_ helps to infer the types more literally, without the worry about being too specific.

With this in mind, it would be intuitive and expected to set the output of *.json modules to be read-only, forbidding any mutable operation on them. This would make it work just like it currently is working with runtime objects and as const assertion.

@parzhitsky I think most would agree on the 1st suggestion, as it is coherent with the present as const statement, and as it is the narrowest option (other types can easily be derived from it if needed).

This is getting even more valuable after Variadic Tuple Types since we can create more types based on the json values, think of json files used for localization so we can extract interpolation. Any thoughts on implementing this yet?

For the last half-year or so I have been forcing this behavior by having a somewhat kludgy pre-compile step that reads in a config.json like {"thing":"myVal"} and exports it as a config.ts like export const Config = {"thing":"myVal"} as const; and use the resulting type definition on the imported json. (previously I needed to do a lot more, prepending readonly everywhere to get the desired array behavior, but at some point that all became unnecessary). It is very helpful during development!

Configuration will likely vary at runtime and thus the content of the json import cannot be known; nevertheless, a as const compiled json delivers on all of TypeScript's primary design goals. I can report that having used it to wrangle over-sized configuration json, it has been invaluable in:

  • enforcing valid configuration defaults
  • inspecting (with "debugger"-like precision) current configuration values while working on code that consumes it
  • providing quick semantic help with regards to the shape of configuration subtrees when that shape is not yet defined in a TypeScript definition (ie on the consumption side).

That is to say, from a pragmatic perspective, import config from './config.json' as const does most of the things that I find TypeScript most helpful for.

@RyanCavanaugh you set this as "Awaiting more feedback" - it has 110 thumbs up and a load of comments from people who would find the feature useful. Can it be considered now or at least the tags changed? Or does it require more people to add to the emoji's ?

@RyanCavanaugh This feature would be very helpful for json-schema-to-ts. You could define and use JSON schemas on one side (API Gateway, swagger... whatever!), and use them in the TS code to infer the type of valid data. Less code duplication, more consistency, everyone would be happier!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fwanicka picture fwanicka  ·  3Comments

manekinekko picture manekinekko  ·  3Comments

MartynasZilinskas picture MartynasZilinskas  ·  3Comments

bgrieder picture bgrieder  ·  3Comments

Antony-Jones picture Antony-Jones  ·  3Comments