tiagosiebler / bybit-api

Node.js SDK for the Bybit APIs and WebSockets, with TypeScript & browser support.

Home Page:https://www.npmjs.com/package/bybit-api

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

TypeScript not choosing the correct type for Instrument V5

ThijMau opened this issue · comments

When using the following code:

// ... { category: 'linear' }
const [instrumentInfo] = response.result.list;
const stepSize = instrumentInfo.lotSizeFilter.qtyStep;

I get the following error message:

error TS2339: Property 'qtyStep' does not exist on type '{ maxOrderQty: string; minOrderQty: string; qtyStep: string; postOnlyMaxOrderQty?: string | undefined; } | { maxOrderQty: string; minOrderQty: string; qtyStep: string; } | { basePrecision: string; ... 4 more ...; maxOrderAmt: string; }'.
  Property 'qtyStep' does not exist on type '{ basePrecision: string; quotePrecision: string; minOrderQty: string; maxOrderQty: string; minOrderAmt: string; maxOrderAmt: string; }'.

Currently this is fixed by casting the object to LinearInverseInstrumentInfoV5 like so:

// ... { category: 'linear' }
const [instrumentInfo] = response.result.list;
const stepSize = (instrumentInfo as LinearInverseInstrumentInfoV5).lotSizeFilter.qtyStep;

When looking through the code, I see that the problem probably lies on src/types/response/v5-market.ts on lines 113-116, see sample:

export type InstrumentInfoResponseV5 =
  | CategoryCursorListV5<LinearInverseInstrumentInfoV5[], 'linear' | 'inverse'>
  | CategoryCursorListV5<OptionInstrumentInfoV5[], 'option'>
  | CategoryCursorListV5<SpotInstrumeI'mntInfoV5[], 'spot'>;

I've tried fixing this in some local commit but I can't seem to get it working, perhaps my knowledge with TypeScript doesn't reach this far yet. I also would not be surprised if this same issue persists for other methods with a shared return type.

Could someone take a look at this please? Thanks!

const [instrumentInfo] = response.result.list;

Can you share what the full response is here?

console.log('response: ', JSON.stringify(response));

Thanks

Sure, the response is:

{
   "retCode":0,
   "retMsg":"OK",
   "result":{
      "category":"linear",
      "list":[
         {
            "symbol":"BTCUSDT",
            "contractType":"LinearPerpetual",
            "status":"Trading",
            "baseCoin":"BTC",
            "quoteCoin":"USDT",
            "launchTime":"1584230400000",
            "deliveryTime":"0",
            "deliveryFeeRate":"",
            "priceScale":"2",
            "leverageFilter":{
               "minLeverage":"1",
               "maxLeverage":"100.00",
               "leverageStep":"0.01"
            },
            "priceFilter":{
               "minPrice":"0.10",
               "maxPrice":"199999.80",
               "tickSize":"0.10"
            },
            "lotSizeFilter":{
               "maxOrderQty":"100.000",
               "minOrderQty":"0.001",
               "qtyStep":"0.001",
               "postOnlyMaxOrderQty":"1000.000"
            },
            "unifiedMarginTrade":true,
            "fundingInterval":480,
            "settleCoin":"USDT",
            "copyTrading":"normalOnly"
         }
      ],
      "nextPageCursor":""
   },
   "retExtInfo":{
      
   },
   "time":1694439419566
}

Thanks! So the simplest right now is to do an if check on the key difference between the 3 diff response types. Here's how you can tell typescript you're definitely dealing with LinearInverseInstrumentInfoV5:

if (response.result.category === 'linear') {
  const [instrumentInfo] = response.result.list;
  console.log('linear qty step: ', instrumentInfo.lotSizeFilter.qtyStep);
}

That'll give typescript enough information to correctly narrow down the type, so it won't complain anymore when accessing properties only available on that type.

Admittedly, it's a bit annoying when it should only be that type because you provided category: linear in the request...this could be improved in the SDK. One way that I know is to provide different method overloads that show what kind of response is expected depending on which request variation is provided. I've done something similar for one of the methods in my binance SDK:
https://github.com/tiagosiebler/binance/blob/master/src/usdm-client.ts#L180-L194

This could be done here as well to give smarter return types. I'd welcome a PR if you'd like to have a go at improving this method in a similar way. I'd make GetInstrumentsInfoParamsV5 a generic, so you could do something like a request with GetInstrumentsInfoParamsV5<'linear' | 'inverse'> will always return Promise<APIResponseV3WithTime<CategoryCursorListV5<LinearInverseInstrumentInfoV5[], 'linear' | 'inverse'>>>

Thank you for the detailed response. I already tried something along those lines, but not exactly what you described. I'll try to implement this and open a pull request!

Released via #288 - should be available on npm now. Thanks again for spending time on the PR!