refactor: enforce strict backend typing

Enable strict TypeScript and reject non-null and definite-assignment assertions. Declare externally populated DTO fields, preserve runtime validation, and narrow values at their owners.

Verified: backend verify (257 tests), unchanged OpenAPI, and 21 SQLite/HTTP tests on retry. The initial SQLite run had two timeouts; baseline HEAD passed 21 tests as well.
This commit is contained in:
2026-09-13 12:17:46 +03:00
parent 3cf883efbc
commit 346a6f0057
33 changed files with 647 additions and 434 deletions
+97 -75
View File
@@ -14,39 +14,51 @@ export class BondCandlesQueryDto extends StockCandlesQueryDto {}
export class BondCandlesDto extends StockCandlesDto {}
export class BondPositionDto extends StockPositionDto {}
export class BondDto {
@ApiProperty({ format: 'uuid' }) instrumentUid: string;
@ApiProperty({ format: 'uuid' }) declare instrumentUid: string;
@ApiProperty({
description: 'Unambiguous ticker URL segment, or the exact instrument UID.',
})
slug: string;
@ApiProperty() name: string;
@ApiProperty({ type: String, nullable: true }) isin: string | null;
@ApiProperty({ type: String, nullable: true }) classCode: string | null;
@ApiProperty({ type: String, nullable: true }) realExchange: string | null;
@ApiProperty({ type: Boolean, nullable: true }) apiTradeAvailable:
declare slug: string;
@ApiProperty() declare name: string;
@ApiProperty({ type: String, nullable: true }) declare isin: string | null;
@ApiProperty({ type: String, nullable: true }) declare classCode:
string | null;
@ApiProperty({ type: String, nullable: true }) declare realExchange:
string | null;
@ApiProperty({ type: Boolean, nullable: true }) declare apiTradeAvailable:
boolean | null;
@ApiProperty({ type: String, nullable: true }) ticker: string | null;
@ApiProperty({ type: String, nullable: true }) logoName: string | null;
@ApiProperty({ type: String, nullable: true }) currency: string | null;
@ApiProperty({ type: String, nullable: true }) sector: string | null;
@ApiProperty({ type: Number, nullable: true }) lot: number | null;
@ApiProperty({ type: String, nullable: true }) declare ticker: string | null;
@ApiProperty({ type: String, nullable: true }) declare logoName:
string | null;
@ApiProperty({ type: String, nullable: true }) declare currency:
string | null;
@ApiProperty({ type: String, nullable: true }) declare sector: string | null;
@ApiProperty({ type: Number, nullable: true }) declare lot: number | null;
@ApiProperty({ type: String, nullable: true, format: 'date-time' })
historyFrom: string | null;
@ApiProperty({ type: MoneyDto, nullable: true }) nominal: MoneyDto | null;
declare historyFrom: string | null;
@ApiProperty({ type: MoneyDto, nullable: true })
initialNominal: MoneyDto | null;
@ApiProperty({ type: MoneyDto, nullable: true }) aci: MoneyDto | null;
@ApiProperty({ type: String, nullable: true }) maturityDate: string | null;
@ApiProperty({ type: String, nullable: true }) callDate: string | null;
@ApiProperty({ type: String, nullable: true }) bondType: string | null;
@ApiProperty({ type: String, nullable: true }) riskLevel: string | null;
@ApiProperty({ type: Number, nullable: true }) couponQuantityPerYear:
declare nominal: MoneyDto | null;
@ApiProperty({ type: MoneyDto, nullable: true })
declare initialNominal: MoneyDto | null;
@ApiProperty({ type: MoneyDto, nullable: true }) declare aci: MoneyDto | null;
@ApiProperty({ type: String, nullable: true }) declare maturityDate:
string | null;
@ApiProperty({ type: String, nullable: true }) declare callDate:
string | null;
@ApiProperty({ type: String, nullable: true }) declare bondType:
string | null;
@ApiProperty({ type: String, nullable: true }) declare riskLevel:
string | null;
@ApiProperty({ type: Number, nullable: true }) declare couponQuantityPerYear:
number | null;
@ApiProperty({ type: Boolean, nullable: true }) floatingCoupon:
@ApiProperty({ type: Boolean, nullable: true }) declare floatingCoupon:
boolean | null;
@ApiProperty({ type: Boolean, nullable: true }) declare perpetual:
boolean | null;
@ApiProperty({ type: Boolean, nullable: true }) declare amortization:
boolean | null;
@ApiProperty({ type: Boolean, nullable: true }) declare subordinated:
boolean | null;
@ApiProperty({ type: Boolean, nullable: true }) perpetual: boolean | null;
@ApiProperty({ type: Boolean, nullable: true }) amortization: boolean | null;
@ApiProperty({ type: Boolean, nullable: true }) subordinated: boolean | null;
}
export class BondSearchResultDto extends BondDto {
@@ -55,89 +67,99 @@ export class BondSearchResultDto extends BondDto {
nullable: true,
description: 'Clean price in percent of nominal, not money.',
})
pricePercent: string | null;
@ApiProperty({ type: String, nullable: true, format: 'date-time' }) priceAt:
string | null;
declare pricePercent: string | null;
@ApiProperty({ type: String, nullable: true, format: 'date-time' })
declare priceAt: string | null;
}
export class BondQuoteDto {
@ApiProperty({ type: String, nullable: true }) pricePercent: string | null;
@ApiProperty({ type: String, nullable: true }) declare pricePercent:
string | null;
@ApiProperty({
type: MoneyDto,
nullable: true,
description:
'Indicative clean value at the current nominal; no FX conversion.',
})
cleanPrice: MoneyDto | null;
@ApiProperty({ type: MoneyDto, nullable: true }) aci: MoneyDto | null;
declare cleanPrice: MoneyDto | null;
@ApiProperty({ type: MoneyDto, nullable: true }) declare aci: MoneyDto | null;
@ApiProperty({
type: MoneyDto,
nullable: true,
description:
'Indicative clean value plus current accrued interest, excluding fees.',
})
dirtyPrice: MoneyDto | null;
@ApiProperty({ type: String, nullable: true }) changePercentagePoints:
declare dirtyPrice: MoneyDto | null;
@ApiProperty({ type: String, nullable: true })
declare changePercentagePoints: string | null;
@ApiProperty({ type: String, nullable: true }) declare previousCloseDate:
string | null;
@ApiProperty({ type: String, nullable: true }) previousCloseDate:
@ApiProperty({ type: String, nullable: true }) declare tradingStatus:
string | null;
@ApiProperty({ type: String, nullable: true }) tradingStatus: string | null;
@ApiProperty({ type: String, nullable: true, format: 'date-time' }) priceAt:
string | null;
@ApiProperty({ type: [String] }) unavailable: string[];
@ApiProperty({ format: 'date-time' }) observedAt: string;
@ApiProperty({ type: String, nullable: true, format: 'date-time' })
declare priceAt: string | null;
@ApiProperty({ type: [String] }) declare unavailable: string[];
@ApiProperty({ format: 'date-time' }) declare observedAt: string;
}
export class BondCouponDto {
@ApiProperty({ type: String, nullable: true }) number: string | null;
@ApiProperty({ type: String, nullable: true }) paymentDate: string | null;
@ApiProperty({ type: String, nullable: true }) recordDate: string | null;
@ApiProperty({ type: String, nullable: true }) couponType: string | null;
@ApiProperty({ type: Number, nullable: true }) periodDays: number | null;
@ApiProperty({ type: MoneyDto, nullable: true }) perBond: MoneyDto | null;
@ApiProperty({ type: String, nullable: true }) declare number: string | null;
@ApiProperty({ type: String, nullable: true }) declare paymentDate:
string | null;
@ApiProperty({ type: String, nullable: true }) declare recordDate:
string | null;
@ApiProperty({ type: String, nullable: true }) declare couponType:
string | null;
@ApiProperty({ type: Number, nullable: true }) declare periodDays:
number | null;
@ApiProperty({ type: MoneyDto, nullable: true })
estimatedAmount: MoneyDto | null;
@ApiProperty({ enum: ['upcoming', 'past', 'undated'] }) timing:
declare perBond: MoneyDto | null;
@ApiProperty({ type: MoneyDto, nullable: true })
declare estimatedAmount: MoneyDto | null;
@ApiProperty({ enum: ['upcoming', 'past', 'undated'] }) declare timing:
'upcoming' | 'past' | 'undated';
}
export class BondCouponsDto {
@ApiProperty({ type: [BondCouponDto] }) coupons: BondCouponDto[];
@ApiProperty() from: string;
@ApiProperty() to: string;
@ApiProperty() observedAt: string;
@ApiProperty({ type: String, nullable: true }) positionAt: string | null;
@ApiProperty() estimateUnavailable: boolean;
@ApiProperty() positionIsStale: boolean;
@ApiProperty({ type: [BondCouponDto] }) declare coupons: BondCouponDto[];
@ApiProperty() declare from: string;
@ApiProperty() declare to: string;
@ApiProperty() declare observedAt: string;
@ApiProperty({ type: String, nullable: true }) declare positionAt:
string | null;
@ApiProperty() declare estimateUnavailable: boolean;
@ApiProperty() declare positionIsStale: boolean;
}
export class BondYieldDto {
@ApiProperty({ description: 'MOEX effective annual yield in percent.' })
value: string;
@ApiProperty() pricePercent: string;
@ApiProperty({ format: 'date' }) date: string;
declare value: string;
@ApiProperty() declare pricePercent: string;
@ApiProperty({ format: 'date' }) declare date: string;
@ApiProperty({
type: String,
nullable: true,
description: 'MOEX yield target, e.g. MATDATE or OFFERDATE.',
})
dateType: string | null;
@ApiProperty({ format: 'date-time' }) tradeAt: string;
declare dateType: string | null;
@ApiProperty({ format: 'date-time' }) declare tradeAt: string;
}
export class BondCreditRatingDto {
@ApiProperty({ enum: ['NCR'] }) agency: 'NCR';
@ApiProperty({ type: String, nullable: true }) value: string | null;
@ApiProperty({ enum: ['active', 'withdrawn'] }) status:
@ApiProperty({ enum: ['NCR'] }) declare agency: 'NCR';
@ApiProperty({ type: String, nullable: true }) declare value: string | null;
@ApiProperty({ enum: ['active', 'withdrawn'] }) declare status:
'active' | 'withdrawn';
@ApiProperty({ format: 'date' }) date: string;
@ApiProperty({ format: 'uri' }) sourceUrl: string;
@ApiProperty({ format: 'date-time' }) observedAt: string;
@ApiProperty({ format: 'date' }) declare date: string;
@ApiProperty({ format: 'uri' }) declare sourceUrl: string;
@ApiProperty({ format: 'date-time' }) declare observedAt: string;
}
export class BondMetricsDto {
@ApiProperty({ type: BondCreditRatingDto, nullable: true })
creditRating: BondCreditRatingDto | null;
@ApiProperty({ type: MoneyDto, nullable: true }) coupon: MoneyDto | null;
@ApiProperty({ type: String, nullable: true, format: 'date' }) couponDate:
declare creditRating: BondCreditRatingDto | null;
@ApiProperty({ type: MoneyDto, nullable: true })
declare coupon: MoneyDto | null;
@ApiProperty({ type: String, nullable: true, format: 'date' })
declare couponDate: string | null;
@ApiProperty({ type: String, nullable: true }) declare couponType:
string | null;
@ApiProperty({ type: String, nullable: true }) couponType: string | null;
@ApiProperty({ type: Number, nullable: true }) couponPeriodDays:
@ApiProperty({ type: Number, nullable: true }) declare couponPeriodDays:
number | null;
@ApiProperty({
type: String,
@@ -145,11 +167,11 @@ export class BondMetricsDto {
description:
'Next coupon × 365 / period days / indicative clean price × 100. Not yield to maturity.',
})
couponYieldPercent: string | null;
declare couponYieldPercent: string | null;
@ApiProperty({ type: String, nullable: true, format: 'date-time' })
couponYieldPriceAt: string | null;
declare couponYieldPriceAt: string | null;
@ApiProperty({ type: BondYieldDto, nullable: true })
effectiveYield: BondYieldDto | null;
@ApiProperty({ type: [String] }) unavailable: string[];
@ApiProperty({ format: 'date-time' }) observedAt: string;
declare effectiveYield: BondYieldDto | null;
@ApiProperty({ type: [String] }) declare unavailable: string[];
@ApiProperty({ format: 'date-time' }) declare observedAt: string;
}