Struggling to accurately import JSON with TypeScript typings

I am facing a challenge with the file test.json that I want to import in a typed form.

{
  "items": [
    {
      "kind": "youtube#video",
      "thumbnails": {
        "default": {
          "url": "https://i.ytimg.com/vi/WnVAkK876rA/default.jpg",
          "width": 120,
          "height": 90
        }
      }
    },
    {
      "kind": "youtube#video",
      "thumbnails": {
        "default": {
          "url": "https://i.ytimg.com/vi/jhTpc7nFtfI/default.jpg",
          "width": 120,
          "height": 90
        },
        "maxres": {
          "url": "https://i.ytimg.com/vi/jhTpc7nFtfI/maxresdefault.jpg",
          "width": 1280,
          "height": 720
        }
      }
    }
  ]
}

This code snippet is from my App.tsx:

import { TestData } from './Types';
import * as testData from './test.json'
const ttt: TestData = testData;

This relates to Types.ts

export interface TestData {
  items: TestDataPiece[]
}

export interface TestDataPiece {
  kind: string,
  thumbnails: {[key: string]: {
    url: string,
    width: number,
    height: number
  }}
}

In the tsconfig.json, I have set

"resolveJsonModule": true
. However, an error occurred:

[tsl] ERROR in C:\Users\home\IdeaProjects\yt-glasat\src\App.tsx(11,7)
      TS2322: Type '{ items: ({ kind: string; thumbnails: { default: { url: string; width: number; height: number; }; maxres?: undefined; }; } | { kind: string; thumbnails: { default: { url: string; width: number; height: number; }; maxres: { ...; }; }; })[]; }' is not assignable to type 'TestData'.
  Types of property 'items' are incompatible.
    Type '({ kind: string; thumbnails: { default: { url: string; width: number; height: number; }; maxres?: undefined; }; } | { kind: string; thumbnails: { default: { url: string; width: number; height: number; }; maxres: { ...; }; }; })[]' is not ...

Answer №1

The issue lies in the fact that the data's inferred type contains a maxres property that is marked as optional, resulting in a type of

{url: string; width: number; height: number} | undefined
. However, your thumbnail object type does not permit undefined values for objects with the index signature.

To resolve this error, you have a few options:

  1. Ensure that all JSON objects include a maxres property;
  2. Permit undefined on thumbnail objects;
  3. Utilize a type assertion during assignment, potentially coupled with a runtime check.

If option #1 is not feasible and you prefer to avoid option #2, then a type assertion like this may be necessary:

const ttt = testData as TestData;

Type assertions should generally be used as a last resort. You can enhance this approach by implementing a runtime check to validate that testData conforms to the interface using either a type guard function or a type assertion function.

For example:

function assertIsValidTestData(data: any): asserts data is TestData {
    if (!Array.isArray(data) ||
        data.some(element => {
            return typeof element !== "object" ||
                   typeof element.kind !== "string" ||
                   typeof element.thumbnails !== "object" ||
                   Object.values(element.thumbnails).some(thumbnail => {
                       return typeof thumbnail !== "object" ||
                              typeof thumbnail.url !== "string" ||
                              typeof thumbnail.width !== "number" ||
                              typeof thumbnail.height !== "number";
                   })
        })) {
        throw new Error(`Test data is not valid`);
    }
}

Then proceed with:

assertIsValidTestData(testData);
const ttt: TestData = testData;

Answer №2

To solve your problem, you can utilize a type assertion like this:

const newData = myData as MyDataType;

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to generate a dynamically interpolated string in JavaScript?

I'm currently developing a reusable UI component and am exploring options to allow the user of this component to provide their own template for a specific section within it. Utilizing TypeScript, I have been experimenting with string interpolation as ...

Categorize messages based on the date they were last read in Angular

I am looking to organize my chat application messages by date, similar to the layout in Microsoft Teams app. Here is an example of the message data: [ { "id": 577, "source": { "userID": 56469, ...

Can a generic type be utilized to instantiate an object?

In my code, I have a class named Entity as shown below: class Entity { constructor(readonly someValue: string) {} someFunction() {} } Now, I am trying to create a class that will handle these entities and be able to create instances of them. In or ...

Angular and Bootstrap work hand in hand to provide a seamless user experience, especially

I have been exploring ways to easily close the modal that appears after clicking on an image. My setup involves using bootstrap in conjunction with Angular. <img id="1" data-toggle="modal" data-target="#myModal" src='assets/barrel.jpg' alt=&a ...

Indeed / Applying dynamic keys for validation with Formik

Attempting to validate a form with a changing number of fields can be challenging. This scenario involves receiving data from an API that dictates how many input rows are displayed. Each row requires a user input to progress, making validation crucial. Th ...

Solving the issue of "Property does not exist on type 'never'" in a program involves identifying the root cause of

Issue An error message related to .cropper is occurring with the code snippet below. Error Message The property 'cropper' does not exist on type 'never'. Query How can I resolve the error associated with type 'never'? B ...

Exploring the benefits of leveraging TypeScript with AWS NodeJS for improved stacktrace visibility over traditional JavaScript

I'm contemplating the idea of transitioning my existing JavaScript codebase to incorporate TypeScript in NodeJS. One aspect that I am concerned about is being able to view the stack trace in AWS CloudWatch (request log) in case an error occurs during ...

Empty spaces are mandatory here

When experimenting with declaring a function that must be called with a specific context (undefined, null, or global), I made an interesting discovery. I noticed that when declaring a function with this: void, it can be called with any context. However, if ...

Setting up APIGateway for CORS with the CDK: A Step-by-Step Guide

My API relies on AWS ApiGateway with an underlying AWS Lambda function provisioned through the CDK. The default CORS settings for the API are as follows: const api = new apiGateway.RestApi(this, "comments-api", { defaultCorsPreflightOptions: { ...

Experiencing difficulty in transferring array information from a parent component to a child component within an

I'm currently working on a task where I need to pass data from a parent component to a child component. The data consists of an array that is nested within another array. parent.component.html <div *ngFor="let parent of parentArray; index as ...

Validating patterns in Angular without using a form

Seeking guidance on validating user input in Angular6 PrimeNG pInputText for a specific URL pattern, such as , possibly triggered on blur event. This particular field used to be part of a form but has since been relocated to a more complex 6-part form int ...

Is there a way to correct Typescript's misunderstanding of the interface from a JSON file that has been imported?

The structure of my JSON file is as follows: [ { "keys": [ { "date": "2019-06-25T17:33:39.000Z" } ], "tag": null }, { "keys": [], "tag": "stringvalue" } ] Upon importing the file, Typescript assumes that ke ...

Retrieve all records from the database using a Sequelize query that fall within the timeframe specified by the start

Currently, I'm attempting to retrieve data from a database using Sequelize, filtering for items that were created within a specific date range. Despite implementing the $between operator in my query, I'm facing difficulties as I'm not receiv ...

Mastering regular expressions in TypeScript

My goal is to perform linting on staged files that are either .ts or .tsx and located within the src folder. I am aware that for selecting all js files one can use "*.js": [--list of commands--] inside the lint staged property. I'm curious to learn m ...

Vue 3 - Child Component Script Not Updating with Reactive Prop Changes

I am facing an issue where I am trying to pass a reactive data as a prop to a child component in Vue 3. The data updates correctly in the child component's template, but it does not reflect in the child component's script. In the parent component ...

An issue has occurred: Unable to locate a supporting object 'No result' of type 'string'. NgFor is only compatible with binding to Iterables like Arrays

I am attempting to utilize this code to post data from a web service. service.ts public events(id: string): Observable<Events> { ...... return this.http.post(Api.getUrl(Api.URLS.events), body, { headers: headers }) .map((re ...

Encountering an issue when attempting to upgrade to Angular 9: Error arising in metadata creation for exported symbol

I am currently in the process of upgrading my Angular 8 application to Angular 9. When running the migration command, I encountered the following issue: Undecorated classes with DI migration. As of Angular 9, it is no longer supported to use Angular ...

Fulfill the promise once all map requests have been completed

Currently, my focus is on developing a bookmark page that retrieves bookmark results with the respective restaurant IDs. Once the response is mapped, I populate an array with objects. My objective is to ultimately resolve the entire array in order to mani ...

What is the cause of the display name missing in the Material-UI Typescript ListItemLink example when using React.forwardRef?

Explore the Material-UI documentation guide on incorporating Typescript in an example demonstrating the creation of a ListItemLink component: Visit the official documentation function ListItemLink(props: ListItemLinkProps) { const { icon, primary, to ...

If the parameter type is "never", the ReturnType will be "any"

short tale const xyz = (a:never):number=>1 type b = ReturnType< typeof xyz> //any const xyz2 = (a:number, b:never):number=>1 type b2 = ReturnType< typeof xyz2> //any playground If any parameter type is never, the ReturnType becom ...