Reputation: 900
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].
Upvotes: 0
Views: 668
Reputation: 2534
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.
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
}
}
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.
Upvotes: 3