Reputation: 39018
I have an array called combinedMarkets
which is either a combination of 5 or 3 different market arrays. All these arrays have the following interface IMarketAsset[]
:
export interface IMarketAsset {
exchange: string;
base: string;
quote: string;
price_quote: string;
timestamp: string;
}
Here is where the typescript error occurs:
const combinedMarkets = asset !== 'BTC' && asset !== 'ETH' ?
btcMarkets.concat(ethMarkets).concat(marketUSD).concat(marketUSDC).concat(marketUSDT) :
marketUSD.concat(marketUSDC).concat(marketUSDT);
const filteredMarkets = combinedMarkets.length > 0 ? filterByUSDbase(asset, combinedMarkets) : [];
Argument of type '({ price_quote: string; exchange: string; base: string; quote: string; timestamp: string; } | undefined)[]' is not assignable to parameter of type 'IMarketAsset[]'. Type '{ price_quote: string; exchange: string; base: string; quote: string; timestamp: string; } | undefined' is not assignable to type 'IMarketAsset'. Type 'undefined' is not assignable to type 'IMarketAsset'.ts(2345)
const combinedMarkets: ({ price_quote: string; exchange: string; base: string; quote: string; timestamp: string; } | undefined)[]
Why is combinedMarkets an array of either an object of type IMarketAsset
or undefined?
// Filter by BTC, ETH, USD, USDT or USDC prices
// If asset has BTC/ETH pairing, obtain exchange BTC/ETH price to calculate assets USD/USDT value
export const combineExchangeData =
(asset: string, { marketBTC, marketETH, marketUSD, marketUSDT, marketUSDC }: IGetMarketsRes) => {
const btcBasedExchanges = marketBTC.filter((market: IMarketAsset) => market.base === asset);
const ethBasedExchanges = marketETH.filter((market: IMarketAsset) => market.base === asset);
const btcUSDTprices = marketUSDT.filter((market: IMarketAsset) => market.base === 'BTC');
const btcUSDprices = marketUSD.filter((market: IMarketAsset) => market.base === 'BTC');
const ethUSDTprices = marketUSDT.filter((market: IMarketAsset) => market.base === 'ETH');
const ethUSDprices = marketUSD.filter((market: IMarketAsset) => market.base === 'ETH');
const btcPricedMarkets = filterByExchangeBase(btcBasedExchanges, btcUSDTprices, btcUSDprices);
const ethPricedMarkets = filterByExchangeBase(ethBasedExchanges, ethUSDTprices, ethUSDprices);
const btcMarkets = btcPricedMarkets.filter((market) => R.not(R.isNil(market)));
const ethMarkets = ethPricedMarkets.filter((market) => R.not(R.isNil(market)));
const combinedMarkets = asset !== 'BTC' && asset !== 'ETH' ?
btcMarkets.concat(ethMarkets).concat(marketUSD).concat(marketUSDC).concat(marketUSDT) :
marketUSD.concat(marketUSDC).concat(marketUSDT);
console.log('combinedMarkets', combinedMarkets);
const filteredMarkets = combinedMarkets.length > 0 ? filterByUSDbase(asset, combinedMarkets) : [];
console.log('filteredMarkets', filteredMarkets);
if (R.isEmpty(filteredMarkets)) return [];
return filteredMarkets.map((market: IMarketAsset) => {
if (market) {
return {
...market,
price_quote: formatPrice(market.price_quote)
}
}
});
};
Here are the 2 other util functions I use in the main function. Also I have narrowed down the problem to the btcMarkets
and ethMarkets
arrays. So looking at filterByExchangeBase
.
import * as R from 'ramda'
import { USD_CURRENCIES } from '../shared/constants/api'
import { IMarketAsset } from '../shared/types'
const calculateBasePrice = (assetBtcPrice: string | number, btcPrice: string | number) =>
(Number(assetBtcPrice) * Number(btcPrice)).toString();
export const filterByExchangeBase =
(exchanges: IMarketAsset[], usdtExchanges: IMarketAsset[], usdExchanges: IMarketAsset[]) =>
exchanges.map((exchange) => {
let basePriced = usdtExchanges.filter((btcExchange) => btcExchange.exchange === exchange.exchange)[0];
if (!basePriced) {
basePriced = usdExchanges.filter((btcExchange) => btcExchange.exchange === exchange.exchange)[0];
}
if (basePriced) {
const { price_quote: assetBtcPrice } = exchange;
const { price_quote: btcPrice } = basePriced;
return {
...exchange,
price_quote: calculateBasePrice(assetBtcPrice, btcPrice)
}
}
});
export const filterByUSDbase = (asset: string, combinedMarkets: IMarketAsset[] | undefined) => {
if (!combinedMarkets) return [];
return R.not(R.any(R.equals(asset))(USD_CURRENCIES))
? combinedMarkets.filter((marketAsset: IMarketAsset) => {
if (marketAsset && marketAsset.base) {
return marketAsset.base === asset;
}
}) : [];
}
Upvotes: 4
Views: 13884
Reputation: 39018
The problem was that in my filterByExchangeBase
util function, I used .map
instead of .filter
which would result in some undefined
objects in that array. Switching to filter made sure that only existing items would make it into the array.
...
Refactored the logic to make sure that btcMarkets and ethMarkets aren't used if they will be empty.
export const combineExchangeData =
(asset: string, { marketBTC, marketETH, marketUSD, marketUSDT, marketUSDC }: IGetMarketsRes) => {
const btcBasedExchanges = marketBTC.filter((market: IMarketAsset) => market.base === asset);
const ethBasedExchanges = marketETH.filter((market: IMarketAsset) => market.base === asset);
const btcUSDTprices = marketUSDT.filter((market: IMarketAsset) => market.base === 'BTC');
const btcUSDprices = marketUSD.filter((market: IMarketAsset) => market.base === 'BTC');
const ethUSDTprices = marketUSDT.filter((market: IMarketAsset) => market.base === 'ETH');
const ethUSDprices = marketUSD.filter((market: IMarketAsset) => market.base === 'ETH');
const btcPricedMarkets = notBTCorETH(asset) ? filterCryptoBase(btcBasedExchanges, btcUSDTprices, btcUSDprices) : [];
const ethPricedMarkets = notBTCorETH(asset) ? filterCryptoBase(ethBasedExchanges, ethUSDTprices, ethUSDprices) : [];
const btcMarkets = R.not(R.isEmpty(btcPricedMarkets)) ? btcPricedMarkets.filter((market: IMarketAsset) => R.not(R.isNil(market))) : [];
const ethMarkets = R.not(R.isEmpty(ethPricedMarkets)) ? ethPricedMarkets.filter((market: IMarketAsset) => R.not(R.isNil(market))) : [];
const combinedMarkets = notBTCorETH(asset) ?
btcMarkets.concat(ethMarkets).concat(marketUSD).concat(marketUSDC).concat(marketUSDT) :
marketUSD.concat(marketUSDC).concat(marketUSDT);
const filteredMarkets = filterByUSDbase(asset, combinedMarkets);
if (R.isEmpty(filteredMarkets)) return [];
return filteredMarkets.map((market: IMarketAsset) => ({
...market,
price_quote: formatPrice(market.price_quote)
}));
};
export const filterCryptoBase =
(exchanges: IMarketAsset[] | any, usdtExchanges: IMarketAsset[], usdExchanges: IMarketAsset[]) => {
return exchanges.map((exchange: IMarketAsset) => {
let basePriced = usdtExchanges.filter((btcExchange) => btcExchange.exchange === exchange.exchange)[0];
if (!basePriced) {
basePriced = usdExchanges.filter((btcExchange) => btcExchange.exchange === exchange.exchange)[0];
}
if (exchange && basePriced && exchange.price_quote && basePriced.price_quote) {
const { price_quote: assetBtcPrice } = exchange; // Asset price in BTC/ETH
const { price_quote: usdPrice } = basePriced; // BTC/ETH price in USDT/USD
const price_quote = calculateBasePrice(assetBtcPrice, usdPrice).toString();
return {
...exchange,
price_quote
}
}
return null;
});
}
Upvotes: -1
Reputation: 845
Try defining the type as an array for each option, instead of combining them:
const combinedMarkets: {
price_quote: string;
exchange: string;
base: string;
quote: string;
timestamp: string;
}[] | undefined[] = [];
Upvotes: 0
Reputation: 276269
Why is combinedMarkets an array either and object of type IMarketAsset or undefined
Because any of the marketXXX
arrays, is an array of IMarketAsset | undefined
(instead of just IMarketAsset
.
Upvotes: 1