data.json
{
"property1": "value 1",
"property2": "value 2"
}
In Javascript
let a = await fetch('./data.json');
I fetch the file into javascript like above ☝🏼.
Is that possible a. to show property1 and property2 in auto-complete?
You can use TypeScript for this:
interface Data { property1: string; property2: string; }
let a = await fetch('./data.json').then(res => res.json() as Promise<Data>);
TypeScript Playground
Related
I have the following mainObject which contains key and value pairs and I would like to use this object's value as a key to an interface or state. The following I provided an example of an interface.
//Main Object
export const mainObject = {
NAME:'name',
AGE:'age'
}
Now I want main object values as a key to an interface
interface sample {
'name':string //i want to achieve like this
}
//modifying the below
import mainObject from '';
interface sample {
mainObject.NAME:string //will give error, need to modify this
}
You can do that if mainObject is declared as a runtime constant (not just the mainObject binding, but the object it contains), via as const:
export const mainObject = {
NAME: "name",
AGE: "age",
} as const;
//^^^^^^^^
Then you can get the type of that object via typeof:
type MainObjectType = typeof mainObject;
Then you can use that type to create a mapped type for Sample:
type Sample = {
[key in MainObjectType[keyof MainObjectType]]: string;
};
With your current definition of mainObject, that would make this code valid:
const x: Sample = {
name: "something",
age: "something else",
};
Playground link
If you changed the properties in mainObject, the types would change, and x would need to be updated to have the new properties. For example, if we added EXAMPLE: "example", to mainObject but didn't update x above, we'd get an error that x was missing the example property — playground example.
I want to parse JSON property as a Map key.
Here is JSON string:
const jsonString = `{
"DDBProps": {
"us-east-1": {
"test": "2",
"prod": "100"
},
"us-west-2": {
"test": "2",
"prod": "100"
}
},
"DDBProps2": {
"us-east-1": {
"test": "2",
"prod": "200"
},
"us-west-2": {
"test": "2",
"prod": "200"
}
}
}`
So, I want these "DDBProps", "DDBProps2", ... "DDBPropsX" to be a Map key, and value to be nested Map. Essentially, something like Map<string, Map<string, Map<string, string>>>. I want this nest map structure because I need to get the number (e.g. "2", "100", "200" in this JSON) based on the input: DDBPropsType, region, stage.
So I declared interfaces:
interface ThroughputMap {
throughputMappings: Map<string,RegionMap>
}
interface RegionMap {
regionMappings: Map<string, StageMap>
}
interface StageMap {
stageMappings: Map<string, string>
}
// parse
const ddbScaleMap: ThroughputMap = JSON.parse(ddbScalingMappingsJson);
However, this doesn't parse the JSON to correct structure:
console.log(ddbScaleMap)
// output below
{
"DDBProps": {
"us-east-1": {"test": "2","prod": "100"},
"us-west-2": {"test": "2","prod": "100"}
},
"DDBProps2": {
"us-east-1": {"test": "2","prod": "200"},
"us-west-2": {"test": "2","prod": "200"}
}
}
console.log(ddbScaleMap.throughputMappings) // output undefined
I can't parse property as Map key.
I tried:
const ddbScaleMap = new Map<string, Map<string, Map<string, string>>>(Object.fromEntries(JSON.parse(jsonString)))
This didn't parse correctly in nested value. Meaning, ddbScaleMap.get("DDBProps").get("us-west-2"); .get("us-west-2") cannot be called.
So apparently, there is a way to access nested value easily without converting to a map. In TS, you use keyof typeof to access nested value like this:
function getThroughputByKey(key: string, region: string, stage: string): number {
const defaultThroughputValue = 1;
const regionToStageMap = ddbScalingMappingsObject[key as keyof typeof ddbScalingMappingsObject];
const stageToThroughputMap = regionToStageMap[region as keyof typeof regionToStageMap];
const throughputString = stageToThroughputMap[stage as keyof typeof stageToThroughputMap];
const throughput = Number(throughputString);
return throughput || defaultThroughputValue;
}
I'm trying construct the class's object from the array.
import searchData from '../somepath/data.json';
And the data.json file have the following data.
[
{
"name": "London"
},
{
"name": "Paris",
}
]
Then I'm looping it using the forEach.
searchData.forEach((obj: OneClass) => {
});
Here is the class
export class OneClass{
readonly name: string;
}
It is not working well, I'm getting some kind of type error.
But when I just change the type of searchData to any then it works perfectly,
const searchData: any = [
{
'name': 'a'
},
{
'name': 'b'
},
];
Note: some code are missing due to privacy restriction, so just trying to understand how the construct the object of typescript class using array objects.
Edited:
The json data was importing using the following method.
import searchData from '../somepath/data.json';
You can either annotate your array explicitly ..
// tell TS that the variable holds an array of objects
// shaped like instances of OneClass
const searchData: OneClass[] = [
{
name: 'a'
},
{
name: 'b'
},
];
or use a so-called const assertion.
const myConstArr = [
{
name: 'bar'
} as const
// the `as const` tells TS that the value of `name` is not going to change
// (that it is a "string literal type" and not just of type `string`)
]
Have a look at the concept of Type Narrowing.
Here is a playground for you to check it out.
You should type your array
const searchData: OneClass[] = [
{
'name': 'a'
},
{
'name': 'b'
},
];
If what you are doing is trying to create an instance of the class for each item in your array of objects, you need to map and call the constructor:
interface SearchDatum {
name: string;
}
const searchData: SearchDatum[] = [
{
'name': 'a'
},
{
'name': 'b'
},
];
class OneClass {
readonly name: string;
constructor({ name }: SearchDatum) {
this.name = name;
}
}
const classInstances: OneClass[] = searchData.map((datum) => new OneClass(datum));
Here is a Typescript playground link
I am using the "resolveJsonModule" feature in typescript 2.9 in order to import a json file. Let's say the json file looks like this:
{
"Nabokov": {
"Pale Fire": {
"Pages": "200",
"Edition": "Paperback",
},
"Pnin": {
"Pages": "150",
"Edition": "Hardcover"
},
"Lolita": {
"Pages": "150",
"Edition": "Paperback",
"Year": "1955"
}
},
"Joyce": {
"Ulysses": {
"Pages": "800",
"Language": "English"
},
"Finnegan's Wake": {
"Pages": "1200",
"Language": "Gibberish"
}
}
}
and i am importing it by:
import catalog from '../resources/catalog.json'
Okay, so when this gets imported, typescript will automatically define types for this. This becomes problematic for me when I am trying to write, for example, a function that can return the info for a author/book.
I want to just do
function getBook(author: string, title: string) {
return catalog[author][title]
}
I get a "Element implicitly has an 'any' type because expression of type 'string' ..." So it wants me to define author as "Nabokov" | "Joyce" but
since the JSON file will be forever expanding, and I don't know what's going to be in it at any time, I want to "genericize" the type for the imported object so that it's just something like [key: string].
Try the following:
type C = typeof catalog;
function getBook<T extends keyof C>(author: T, title: keyof C[T]) {
return catalog[author][title]
}
getBook("Nabokov", "Lolita"); // OK
getBook("Joyce", "Lolita"); // Not OK
Here is a playground to demonstrate.
Update
Based on your use case, you shouldn't restrict the types of the parameters, and you should just do this:
function getBook(author: string, title: string) {
try {
return (catalog as any)[author][title]
} catch(e) {
// do error handling here
}
}
Update 2
The method mentioned above is more or less bypassing TypeScript. If one really want to ensure type-safety, here's the genuine TypeScript approach to do the same thing.
type B = {
Pages: string;
Edition?: string;
Year?: string;
Language?: string;
};
type C = {
[index: string]: {
[index:string]: B
}
};
function getBook(author: string, title: string): B | undefined {
const cat: C = catalog;
if(author in cat && title in cat[author]) {
return cat[author][title];
} else {
// do error handling here
return undefined;
}
}
See this playground.
I want to learn TypeScript.
I have a JSON dictionary returned by the sentry method event_from_exception() (Python).
I would like to format it as nice HTML with expandable local variables and pre_ and post_context. The result should look roughly like this:
Here is an example json:
{
"exception": {
"values": [
{
"stacktrace": {
"frames": [
{
"function": "main",
"abs_path": "/home/modlink_cok_d/src/sentry-json.py",
"pre_context": [
"from sentry_sdk.utils import event_from_exception",
"",
"def main():",
" local_var = 1",
" try:"
],
"lineno": 9,
"vars": {
"exc": "ValueError()",
"local_var": "1"
},
"context_line": " raise ValueError()",
"post_context": [
" except Exception as exc:",
" event, info = event_from_exception(sys.exc_info(), with_locals=True)",
" print(json.dumps(event, indent=2))",
"",
"main()"
],
"module": "__main__",
"filename": "sentry-json.py"
}
]
},
"type": "ValueError",
"value": "",
"module": "exceptions",
"mechanism": null
}
]
},
"level": "error"
}
How could this be done with TypeScript?
Create the schema for your data. This will help you on working with TypeScript and IDE.
You can use https://app.quicktype.io which give you.
export interface Welcome {
exception: Exception;
level: string;
}
export interface Exception {
values: Value[];
}
export interface Value {
stacktrace: Stacktrace;
type: string;
value: string;
module: string;
mechanism: null;
}
export interface Stacktrace {
frames: Frame[];
}
export interface Frame {
function: string;
abs_path: string;
pre_context: string[];
lineno: number;
vars: Vars;
context_line: string;
post_context: string[];
module: string;
filename: string;
}
export interface Vars {
exc: string;
local_var: string;
}
Render HTML from your data.
You can use template literal
if you do not have prefer web framework (React, Vue).
const data = JSON.parse(json);
const html = `
<div>${data.exception.values.map(value => `
<div>${value.stacktrace.frames.map(frame => `
<div>
<pre>${frame.abs_path} in ${frame.function}</pre>
<div style="margin-left:2rem">
${frame.pre_context.map((line, i) =>`
<pre>${frame.lineno + i - frame.pre_context.length}. ${line}</pre>
`).join("")}
<pre><strong>${frame.lineno}. ${frame.context_line}</strong></pre>
${frame.post_context.map((line, i) => `
<pre>${frame.lineno + i + 1}. ${line}</pre>
`).join("")}
</div>
</div>
`).join("")}</div>
`).join("")}</div>
`;
document.body.innerHTML = html;
For example: https://codesandbox.io/s/52x8r17zo4
To do this on your own without a framework I would create a class for each element in the json. Then I would have a toHTML(domParent) method on each class that iterated over it's sub components:
class Stacktrace {
frames: Frame[];
type: string;
value: string;
module: string;
mechanism: string;
toHTML(domParent) {
for (let frame of Frame) {
frame.toHTML(domParent);
}
domParent.addChild(`<div>${type}</div>`);
domParent.addChild(`<div>${value}</div>`);
domParent.addChild(`<div>${module}</div>`);
domParent.addChild(`<div>${mechanism}</div>`);
}
}
This is just pseudocode but should get you on the right track.
Option 1. save the file as .json and open with browser (I tried with FireFox) you should be able to see it in a very nice format as in pic posted by you.
option 2. use JavaScript to dynamically create DOM objects depending on the JSON. Refer this and this.
/*
Following are some hints for creating your script
parse JSON
use recursive function to create div or p tags as per your preference.
also add click event listener on each element to expand/collapse the child views.
*/
Hope this helps.