我正在尝试创建一个只能接受一组定义的键的对象类型。在下面的示例中,//************************************************************************************************/
//* VR Smart Grid Lite for mt5.mq5 */
//* Copyright 2019, Trading-go Project. */
//* Author: Voldemar, Version: 14.05.2019, Site https://trading-go.ru */
//************************************************************************************************/
//* */
//************************************************************************************************/
//VR Smart Grid https://www.mql5.com/ru/market/product/28140
//VR Smart Grid Lite https://www.mql5.com/ru/code/20223/
//VR Smart Grid MT5 https://www.mql5.com/ru/market/product/38626/
//VR Smart Grid Lite MT5 https://www.mql5.com/ru/code/25528/
//Blog RU https://www.mql5.com/ru/blogs/post/726568
//Blog EN https://www.mql5.com/en/blogs/post/726569
//************************************************************************************************/
//| All products of the Author https://www.mql5.com/ru/users/voldemar/seller
//************************************************************************************************/
#property copyright "Copyright 2019, Trading-go Project."
#property link "https://trading-go.ru"
#property version "19.050"
#property description " "
#include <Trade\PositionInfo.mqh> CPositionInfo m_position;
#include <Trade\Trade.mqh> CTrade trade;
//************************************************************************************************/
//* */
//************************************************************************************************/
input int iTakeProfit = 300; // Take Profit (in pips)
input double iStartLots = 0.01; // Start lot
input double iMaximalLots = 2.56; // Maximal Lots
input int iPointOrderStep = 390; // Point order step (in pips)
input int iMinimalProfit = 70; // Minimal profit for close grid (in pips)
input int iMagicNumber = 227; // Magic Number (in number)
input int iSlippage = 30; // Slippage (in pips)
//---
//************************************************************************************************/
//* */
//************************************************************************************************/
int OnInit()
{
Comment("");
trade.LogLevel(LOG_LEVEL_ERRORS);
trade.SetExpertMagicNumber(iMagicNumber);
trade.SetDeviationInPoints(iSlippage);
trade.SetMarginMode();
trade.SetTypeFillingBySymbol(Symbol());
return(INIT_SUCCEEDED);
}
//************************************************************************************************/
//* */
//************************************************************************************************/
void OnTick()
{
double
BuyPriceMax=0,BuyPriceMin=0,BuyPriceMaxLot=0,BuyPriceMinLot=0,
SelPriceMin=0,SelPriceMax=0,SelPriceMinLot=0,SelPriceMaxLot=0;
ulong
BuyPriceMaxTic=0,BuyPriceMinTic=0,SelPriceMaxTic=0,SelPriceMinTic=0;
double
op=0,lt=0,tp=0;
ulong tk=0;
int b=0,s=0;
int total=PositionsTotal();
for(int k=total-1;k>=0;k--)
if(m_position.SelectByIndex(k))
if(m_position.Symbol()==Symbol())
if(m_position.Magic()==iMagicNumber)
if(m_position.PositionType()==POSITION_TYPE_BUY || m_position.PositionType()==POSITION_TYPE_SELL)
{
op=NormalizeDouble(m_position.PriceOpen(),Digits());
lt=NormalizeDouble(m_position.Volume(),2);
tk=m_position.Ticket();
if(m_position.PositionType()==POSITION_TYPE_BUY)
{
b++;
if(op>BuyPriceMax || BuyPriceMax==0)
{
BuyPriceMax = op;
BuyPriceMaxLot = lt;
BuyPriceMaxTic = tk;
}
if(op<BuyPriceMin || BuyPriceMin==0)
{
BuyPriceMin = op;
BuyPriceMinLot = lt;
BuyPriceMinTic = tk;
}
}
// ===
if(m_position.PositionType()==POSITION_TYPE_SELL)
{
s++;
if(op>SelPriceMax || SelPriceMax==0)
{
SelPriceMax = op;
SelPriceMaxLot = lt;
SelPriceMaxTic = tk;
}
if(op<SelPriceMin || SelPriceMin==0)
{
SelPriceMin = op;
SelPriceMinLot = lt;
SelPriceMinTic = tk;
}
}
}
//*************************************************************//
double AwerageBuyPrice=0,AwerageSelPrice=0;
if(b>=2) AwerageBuyPrice=NormalizeDouble((BuyPriceMax*BuyPriceMaxLot+BuyPriceMin*BuyPriceMinLot)/(BuyPriceMaxLot+BuyPriceMinLot)+iMinimalProfit*Point(),Digits());
if(s>=2) AwerageSelPrice=NormalizeDouble((SelPriceMax*SelPriceMaxLot+SelPriceMin*SelPriceMinLot)/(SelPriceMaxLot+SelPriceMinLot)-iMinimalProfit*Point(),Digits());
//*************************************************************//
double BuyLot=0,SelLot=0;
if(BuyPriceMinLot==0) BuyLot=iStartLots; else BuyLot=BuyPriceMinLot*2;
if(SelPriceMaxLot==0) SelLot=iStartLots; else SelLot=SelPriceMaxLot*2;
//*************************************************************//
if(BuyLot>iMaximalLots) BuyLot=iMaximalLots;
if(SelLot>iMaximalLots) SelLot=iMaximalLots;
if(!CheckVolumeValue(BuyLot) || !CheckVolumeValue(SelLot))
return;
//*************************************************************//
MqlRates rates[];
CopyRates(Symbol(),PERIOD_CURRENT,0,2,rates);
MqlTick tick;
if(!SymbolInfoTick(Symbol(),tick))
Print("SymbolInfoTick() failed, error = ",GetLastError());
if(rates[1].close>rates[1].open)
if((b==0) || (b>0 && (BuyPriceMin-tick.ask)>(iPointOrderStep*Point())))
if(!trade.Buy(NormalizeDouble(BuyLot,2)))
Print("OrderSend error #",GetLastError());
if(rates[1].close<rates[1].open)
if((s==0) || (s>0 && (tick.bid-SelPriceMax)>(iPointOrderStep*Point())))
if(!trade.Sell(NormalizeDouble(SelLot,2)))
Print("OrderSend error #",GetLastError());
//*************************************************************//
for(int k=total-1;k>=0;k--)
if(m_position.SelectByIndex(k))
if(m_position.Symbol()==Symbol())
if(m_position.Magic()==iMagicNumber)
if(m_position.PositionType()==POSITION_TYPE_BUY || m_position.PositionType()==POSITION_TYPE_SELL)
{
op=NormalizeDouble(m_position.PriceOpen(),Digits());
tp=NormalizeDouble(m_position.TakeProfit(),Digits());
lt=NormalizeDouble(m_position.Volume(),2);
tk=m_position.Ticket();
if(m_position.PositionType()==POSITION_TYPE_BUY && b==1 && tp==0)
if(!trade.PositionModify(tk,m_position.StopLoss(),NormalizeDouble(tick.ask+iTakeProfit*Point(),Digits())))
Print("OrderModify error #",GetLastError());
if(m_position.PositionType()==POSITION_TYPE_SELL && s==1 && tp==0)
if(!trade.PositionModify(tk,m_position.StopLoss(),NormalizeDouble(tick.bid-iTakeProfit*Point(),Digits())))
Print("OrderModify error #",GetLastError());
if(m_position.PositionType()==POSITION_TYPE_BUY && b>=2)
{
if(tk==BuyPriceMaxTic || tk==BuyPriceMinTic)
if(tick.bid<AwerageBuyPrice && tp!=AwerageBuyPrice)
if(!trade.PositionModify(tk,m_position.StopLoss(),AwerageBuyPrice))
Print("OrderModify error #",GetLastError());
if(tk!=BuyPriceMaxTic && tk!=BuyPriceMinTic && tp!=0)
if(!trade.PositionModify(tk,0,0))
Print("OrderModify error #",GetLastError());
}
if(m_position.PositionType()==POSITION_TYPE_SELL && s>=2)
{
if(tk==SelPriceMaxTic || tk==SelPriceMinTic)
if(tick.ask>AwerageSelPrice && tp!=AwerageSelPrice)
if(!trade.PositionModify(tk,m_position.StopLoss(),AwerageSelPrice))
Print("OrderModify error #",GetLastError());
if(tk!=SelPriceMaxTic && tk!=SelPriceMinTic && tp!=0)
if(!trade.PositionModify(tk,0,0))
Print("OrderModify error #",GetLastError());
}
}
}
//************************************************************************************************/
//* */
//************************************************************************************************/
void OnDeinit(const int reason)
{
}
//************************************************************************************************/
//* */
//************************************************************************************************/
bool CheckVolumeValue(double volume)
{
//--- минимально допустимый объем для торговых операций
double min_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
if(volume<min_volume)
return(false);
//--- максимально допустимый объем для торговых операций
double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
if(volume>max_volume)
return(false);
//--- получим минимальную градацию объема
double volume_step=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP);
int ratio=(int)MathRound(volume/volume_step);
if(MathAbs(ratio*volume_step-volume)>0.0000001)
return(false);
return(true);
}
//************************************************************************************************/
//* */
//************************************************************************************************/
。
假设'1.1.1' | '1.1.2' | '1.1.3'
是集合中的有效字符串。
levelNum, subLevelNum and problemNum
但是,如果我按如下方式编写错误行,则它会起作用:
type AllowedIndexes = '1.1.1' | '1.1.2' | '1.1.3';
type ProblemType = { [index in AllowedIndexes]?: Problem };
let problems: ProblemType = {};
// --> the following line gives the error <--
// `TS2322: Type 'string' is not assignable to type AllowedIndex.`
let ix:AllowedIndexes = `${levelNum}.${subLevelNum}.${problemNum}`;
problems[ix] = new Problem(....)
我走的路正确吗,还是我完全确定对象键属于已定义的集?
答案 0 :(得分:1)
你在这里是对的。
此行
let ix:AllowedIndexes = `${levelNum}.${subLevelNum}.${problemNum}`;
告诉您的TypeScript您正在尝试将模板字符串(类型为string
)分配给期望类型为AllowedIndexes
的值。但是,TypeScript不能确保这始终是正确的,并且需要进行修改:
let ix:AllowedIndexes = `${levelNum}.${subLevelNum}.${problemNum}` as AllowedIndexes;
您明确地告诉它该值确实为AllowedIndexes
。从类型的角度来看,它是正确的,而从逻辑/运行时角度来看,当levelNum
,subLevelNum
或problemNum
变量之一不正确时,它可能是不正确的,但是TypeScript无法知道这个案例。我能想到的对该代码的唯一改进是对Record
使用内置的ProblemType
类型,如下所示:
type ProblemType = Partial<Record<AllowedIndexes, Problem>>;
请注意,我之所以添加Partial
只是因为您使用?
将对象的道具指定为可选道具
答案 1 :(得分:0)
除了使用字符串文字类型的简单相等性之外,没有其他方法可以限制ts中字符串的内容。
也许可以改用元组类型,可以将其约束为特定长度,并使用文字类型为元组中的每个项目指定有效域。另外,如果您的零件以表单字符串开头,则可以使用自定义类型断言或类型保护来缩小类型:
type AllowedIndexesItem = "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "0";
type AllowedIndexes = [AllowedIndexesItem, AllowedIndexesItem, AllowedIndexesItem];
declare let problemNum: string;
declare let subLevelNum: string;
declare let levelNum: string;
function assertAllowedIndexesItem(o: string): asserts o is AllowedIndexesItem {
if (!(o.length == 1 && o >= "0" && o < "9")) {
throw new Error("Invlaid value")
}
}
assertAllowedIndexesItem(levelNum);
assertAllowedIndexesItem(subLevelNum);
assertAllowedIndexesItem(problemNum);
let ix:AllowedIndexes = [levelNum, subLevelNum, problemNum];