Recently, I have started learning TypeScript and I am working on a function to convert arrays from a web request response into objects. I have successfully written the function along with a passing unit test:
import { parseDataToObject } from './ParseResults';
interface ITestPerson {
name: string;
age: number;
}
describe('parseDataToObject', () => {
// Test cases omitted for simplicity
describe('passed data and keys array', () => {
it('returns an array of converted objects matching an interface', () => {
const testData = [['matt', 25], ['dom', 45]];
const testKeys = ['name', 'age'];
const expectToBe: Array<ITestPerson> = [{ name: 'matt', age: 25 }, { name: 'dom', age: 45 }];
expect(parseDataToObject<ITestPerson>({ data: testData, keys: testKeys })).toStrictEqual(expectToBe);
});
});
});
Here is the function that I have implemented:
export function parseDataToObject<T>({ data, keys }: {data: any[][], keys: Array<string>}): Array<T> {
if (data.length === 0) return [];
if (data[0].length !== keys.length) throw new Error("Keys array length doesn't match the data count");
return data.map((dataArr: Array<any>): T => {
let object = {};
for (let i = 0; i < keys.length; i++) {
object[keys[i]] = dataArr[i];
}
return object;
});
}
// The above code resides in ParseResults.ts
Even though the function works fine in jest tests, I'm encountering some TypeScript compilation errors related to type handling.
The main objective is to convert an array of arrays into typed objects. An example of the API response data structure I am dealing with looks like this:
[[1622073600000,"39241.92000000","40411.14000000","37134.27000000","38529.98000000","86547.15879400",1622159999999,"3361413526.05354346",2102182,"42256.01280100","1641681553.52913720","0"],...]
After successful testing, when I try to implement it in my application as shown below:
// Binance.ts
export interface IKlineData {
openTime: number;
open: string;
high: string;
low: string
close: string;
volume: string;
closeTiime: number;
quoteAssetVol: string;
numTrades: number;
takerBuyBaseAssetVol: string;
takerBuyQuoteAssetVol: string;
ignore: string;
}
const klineDataKeys = [
'openTime',
'open',
'high',
'low',
'close',
'volume',
'closeTiime',
'quoteAssetVol',
'numTrades',
'takerBuyBaseAssetVol',
'takerBuyQuoteAssetVol',
'ignore'];
const result = await axiosGet(`https://api.binance.com/api/v3/klines?symbol=${symbol}&interval=1d`);
// Line 68
const klineData: Array<IKlineData> = parseDataToObject<IKlineData>({data: result.data, keys: klineDataKeys});
Unfortunately, TypeScript compiler throws a few errors as follows:
src/index.d/Binance.ts(68,75): error TS2322: Type 'unknown' is not assignable to ty
pe 'any[][]'.
src/index.d/binance.d/ParseResults.ts(7,7): error TS7053: Element implicitly has an
'any' type because expression of type 'string' can't be used to index type '{}'.
No index signature with a parameter of type 'string' was found on type '{}'.
src/index.d/binance.d/ParseResults.ts(9,5): error TS2322: Type '{}' is not assignab
le to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to '{}'
I understand that there might be some unsafe operations within the parseDataToObject
function, but I'm unsure how to rectify these issues. Any help would be greatly appreciated.
Below is a minimal reproducible example for reference:
import axios, { AxiosRequestConfig } from 'axios';
export interface IResponseData<T> {
status: number;
data: T;
}
export interface IKlineData {
openTime: number;
open: string;
high: string;
low: string
close: string;
volume: string;
closeTiime: number;
quoteAssetVol: string;
numTrades: number;
takerBuyBaseAssetVol: string;
takerBuyQuoteAssetVol: string;
ignore: string;
}
const klineDataKeys = [
'openTime',
'open',
'high',
'low',
'close',
'volume',
'closeTiime',
'quoteAssetVol',
'numTrades',
'takerBuyBaseAssetVol',
'takerBuyQuoteAssetVol',
'ignore'];
function axiosGet<T>(url: string): Promise<IResponseData<T>> {
const axiosConfig: AxiosRequestConfig = {
method: 'get',
url,
headers: {
'Content-Type': 'application/json',
},
};
return new Promise<IResponseData<T>>((resolve, reject) => axios(axiosConfig)
.then((response) => {
resolve({ data: response.data, status: response.status });
})
.catch((error) => {
reject(error);
}));
}
(async function () {
const result = await axiosGet(`https://api.binance.com/api/v3/klines?symbol=BTCUSDT&interval=1d`);
const klineData: Array<IKlineData> = parseDataToObject<IKlineData>({data: result.data, keys: klineDataKeys});
}());
function parseDataToObject<T>({ data, keys }: {data: any[][], keys: Array<string>}): Array<T> {
if (data.length === 0) return [];
if (data[0].length !== keys.length) throw new Error("Keys array length doesn't match the data count");
return data.map((dataArr: Array<any>): T => {
let object = {};
for (let i = 0; i < keys.length; i++) {
object[keys[i]] = dataArr[i];
}
return <T>object;
});
}