Can't get TypeScript to import JSON with proper types
I have the following file test.json
that I wish to import it 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 is my App.tsx
:
import { TestData } from './Types';
import * as testData from './test.json'
const ttt: TestData = testData;
This is Types.ts
export interface TestData {
items: TestDataPiece[]
}
export interface TestDataPiece {
kind: string,
thumbnails: {[key: string]: {
url: string,
width: number,
height: number
}}
}
In my tsconfig.json
I use "resolveJsonModule": true
. However I get this error:
[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 assignable to type 'TestDataPiece[]'.
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 assignable to type 'TestDataPiece'.
Type '{ kind: string; thumbnails: { default: { url: string; width: number; height: number; }; maxres?: undefined; }; }' is not assignable to type 'TestDataPiece'.
Types of property 'thumbnails' are incompatible.
Type '{ default: { url: string; width: number; height: number; }; maxres?: undefined; }' is not assignable to type '{ [key: string]: { url: string; width: number; height: number; }; }'.
Property '"maxres"' is incompatible with index signature.
Type 'undefined' is not assignable to type '{ url: string; width: number; height: number; }'.
The problem is that the inferred type of the data has a maxres
property that's optional, which means its type is {url: string; width: number; height: number} | undefined
. But your thumbnail object type doesn't allow undefined
for the values of the object with the index signature.
Any of these would make the error go away:
- Ensure that all of the relevant objects in the JSON have a
maxres
property; or - Allow
undefined
on thumbnail objects; or - Use a type assertion when doing the assignment, perhaps backed by a runtime check
I'm guessing you can't do #1 and don't want to do #2, so you're left with the type assertion:
const ttt = testData as TestData;
Type assertions are generally a last-ditch solution, though; you can back it up with a runtime check to do validation on testData
to ensure it fits the interface using a type guard function or a type assertion function.
For instance:
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`);
}
}
And then
assertIsValidTestData(testData);
const ttt: TestData = testData;
Using a type assertion should work for your case:
const ttt = testData as TestData;