@@ -6,7 +6,10 @@ import { setMathHelpers } from './mathHelpers/setMathHelpers.js';
66import { copySetMathHelpers } from './mathHelpers/copySetMathHelpers.js' ;
77import { copyBagMathHelpers } from './mathHelpers/copyBagMathHelpers.js' ;
88
9- /** @import {Amount, AssetKind, AmountValue, AssetKindForValue, AssetValueForKind, Brand, MathHelpers} from './types.js' */
9+ /**
10+ * @import {CopyBag, CopySet} from '@endo/patterns';
11+ * @import {Amount, AssetKind, AmountValue, AssetKindForValue, AssetValueForKind, Brand, CopyBagAmount, CopySetAmount, MathHelpers, NatAmount, NatValue, SetAmount, SetValue} from './types.js';
12+ */
1013
1114const { quote : q , Fail } = assert ;
1215
@@ -76,26 +79,19 @@ const helpers = {
7679 copyBag : copyBagMathHelpers ,
7780} ;
7881
79- /**
80- * @template {AmountValue} V
81- * @type {(value: V) => AssetKindForValue<V> }
82- */
82+ /** @type {(value: unknown) => 'nat' | 'set' | 'copySet' | 'copyBag' } } */
8383const assertValueGetAssetKind = value => {
8484 const passStyle = passStyleOf ( value ) ;
8585 if ( passStyle === 'bigint' ) {
86- // @ts -expect-error cast
8786 return 'nat' ;
8887 }
8988 if ( passStyle === 'copyArray' ) {
90- // @ts -expect-error cast
9189 return 'set' ;
9290 }
9391 if ( matches ( value , M . set ( ) ) ) {
94- // @ts -expect-error cast
9592 return 'copySet' ;
9693 }
9794 if ( matches ( value , M . bag ( ) ) ) {
98- // @ts -expect-error cast
9995 return 'copyBag' ;
10096 }
10197 // TODO This isn't quite the right error message, in case valuePassStyle
@@ -113,7 +109,7 @@ const assertValueGetAssetKind = value => {
113109 *
114110 * Made available only for testing, but it is harmless for other uses.
115111 *
116- * @template {AmountValue} V
112+ * @template V
117113 * @param {V } value
118114 * @returns {MathHelpers<V> }
119115 */
@@ -198,29 +194,44 @@ const isGTE = (leftAmount, rightAmount, brand = undefined) => {
198194 * the abstract right to participate in a particular exchange.
199195 */
200196const AmountMath = {
197+ // TODO use overloading to handle when Brand has an AssetKind and when it doesn't.
198+ // a AmountForValue utility could help DRY those cases.
201199 /**
202200 * Make an amount from a value by adding the brand.
203201 *
204- * @template {AssetKind} K
205- * @param {Brand<K> } brand
206- * @param {AssetValueForKind<K> } allegedValue
207- * @returns {Amount<K> }
202+ * Does not verify that the Brand's AssetKind matches the value's.
203+ *
204+ * @template {Brand} B
205+ * @template {NatValue | CopySet | CopyBag | SetValue} V
206+ * @param {B } brand
207+ * @param {V } allegedValue
208+ * @returns {B extends Brand<'nat'>
209+ * ? NatAmount
210+ * : V extends NatValue
211+ * ? NatAmount
212+ * : V extends CopySet
213+ * ? CopySetAmount<V['payload'][0]>
214+ * : V extends CopyBag
215+ * ? CopyBagAmount<V['payload'][0][0]>
216+ * : V extends SetValue
217+ * ? SetAmount<V[0]>
218+ * : never}
208219 */
209- // allegedValue has a conditional expression for type widening, to prevent V being bound to a a literal like 1n
210220 make : ( brand , allegedValue ) => {
211221 assertRemotable ( brand , 'brand' ) ;
212222 const h = assertValueGetHelpers ( allegedValue ) ;
213223 const value = h . doCoerce ( allegedValue ) ;
224+ // @ts -expect-error cast
214225 return harden ( { brand, value } ) ;
215226 } ,
216227 /**
217228 * Make sure this amount is valid enough, and return a corresponding valid
218229 * amount if so.
219230 *
220- * @template {AssetKind} K
221- * @param {Brand<K> } brand
222- * @param {Amount<K> } allegedAmount
223- * @returns {Amount<K> }
231+ * @template {Amount} A
232+ * @param {Brand } brand
233+ * @param {A } allegedAmount
234+ * @returns {A }
224235 */
225236 coerce : ( brand , allegedAmount ) => {
226237 assertRemotable ( brand , 'brand' ) ;
@@ -229,15 +240,16 @@ const AmountMath = {
229240 brand === allegedBrand ||
230241 Fail `The brand in the allegedAmount ${ allegedAmount } in 'coerce' didn't match the specified brand ${ brand } .` ;
231242 // Will throw on inappropriate value
243+ // @ts -expect-error cast
232244 return AmountMath . make ( brand , allegedValue ) ;
233245 } ,
234246 /**
235247 * Extract and return the value.
236248 *
237- * @template {AssetKind} K
238- * @param {Brand<K> } brand
239- * @param {Amount<K> } amount
240- * @returns {AssetValueForKind<K> }
249+ * @template {Amount} A
250+ * @param {Brand } brand
251+ * @param {A } amount
252+ * @returns {A['value'] }
241253 */
242254 getValue : ( brand , amount ) => AmountMath . coerce ( brand , amount ) . value ,
243255 /**
@@ -246,29 +258,29 @@ const AmountMath = {
246258 *
247259 * @type {{
248260 * (brand: Brand): Amount<'nat'>;
249- * <K extends AssetKind>(brand: Brand, assetKind: K): Amount<K>;
261+ * <K extends AssetKind>(brand: Brand<K> , assetKind: K): Amount<K>;
250262 * }}
251263 */
252264 makeEmpty : ( brand , assetKind = /** @type {const } */ ( 'nat' ) ) => {
253265 assertRemotable ( brand , 'brand' ) ;
254266 assertAssetKind ( assetKind ) ;
255267 const value = helpers [ assetKind ] . doMakeEmpty ( ) ;
268+ // @ts -expect-error XXX narrowing from function overload
256269 return harden ( { brand, value } ) ;
257270 } ,
258271 /**
259272 * Return the amount representing an empty amount, using another amount as the
260273 * template for the brand and assetKind.
261274 *
262- * @template {AssetKind} K
263- * @param {Amount<K> } amount
264- * @returns {Amount<K> }
275+ * @template {Amount} A
276+ * @param {A } amount
277+ * @returns {A }
265278 */
266279 makeEmptyFromAmount : amount => {
267280 assertRecord ( amount , 'amount' ) ;
268281 const { brand, value } = amount ;
269- // @ts -expect-error cast
270282 const assetKind = assertValueGetAssetKind ( value ) ;
271- // @ts -expect-error cast (ignore b/c erroring in CI but not my IDE)
283+ // @ts -expect-error different subtype
272284 return AmountMath . makeEmpty ( brand , assetKind ) ;
273285 } ,
274286 /**
@@ -291,10 +303,10 @@ const AmountMath = {
291303 * Returns true if the leftAmount equals the rightAmount. We assume that if
292304 * isGTE is true in both directions, isEqual is also true
293305 *
294- * @template {AssetKind} K
295- * @param {Amount<K> } leftAmount
296- * @param {Amount<K> } rightAmount
297- * @param {Brand<K> } [brand]
306+ * @template {Amount} A
307+ * @param {A } leftAmount
308+ * @param {A } rightAmount
309+ * @param {Brand } [brand]
298310 * @returns {boolean }
299311 */
300312 isEqual : ( leftAmount , rightAmount , brand = undefined ) => {
@@ -308,15 +320,16 @@ const AmountMath = {
308320 * amount, it usually means including all of the elements from both left and
309321 * right.
310322 *
311- * @template {AssetKind} K
312- * @param {Amount<K> } leftAmount
313- * @param {Amount<K> } rightAmount
314- * @param {Brand<K> } [brand]
315- * @returns {Amount<K> }
323+ * @template {Amount} A
324+ * @param {A } leftAmount
325+ * @param {A } rightAmount
326+ * @param {Brand } [brand]
327+ * @returns {A }
316328 */
317329 add : ( leftAmount , rightAmount , brand = undefined ) => {
318330 const h = checkLRAndGetHelpers ( leftAmount , rightAmount , brand ) ;
319331 const value = h . doAdd ( ...coerceLR ( h , leftAmount , rightAmount ) ) ;
332+ // @ts -expect-error different subtype
320333 return harden ( { brand : leftAmount . brand , value } ) ;
321334 } ,
322335 /**
@@ -326,25 +339,27 @@ const AmountMath = {
326339 * error. Because the left amount must include the right amount, this is NOT
327340 * equivalent to set subtraction.
328341 *
329- * @template {AssetKind} K
330- * @param {Amount<K> } leftAmount
331- * @param {Amount<K> } rightAmount
332- * @param {Brand<K> } [brand]
333- * @returns {Amount<K> }
342+ * @template {Amount} L
343+ * @template {Amount} R
344+ * @param {L } leftAmount
345+ * @param {R } rightAmount
346+ * @param {Brand } [brand]
347+ * @returns {L extends R ? L : never }
334348 */
335349 subtract : ( leftAmount , rightAmount , brand = undefined ) => {
336350 const h = checkLRAndGetHelpers ( leftAmount , rightAmount , brand ) ;
337351 const value = h . doSubtract ( ...coerceLR ( h , leftAmount , rightAmount ) ) ;
352+ // @ts -expect-error different subtype
338353 return harden ( { brand : leftAmount . brand , value } ) ;
339354 } ,
340355 /**
341356 * Returns the min value between x and y using isGTE
342357 *
343- * @template {AssetKind} K
344- * @param {Amount<K> } x
345- * @param {Amount<K> } y
346- * @param {Brand<K> } [brand]
347- * @returns {Amount<K> }
358+ * @template {Amount} A
359+ * @param {A } x
360+ * @param {A } y
361+ * @param {Brand } [brand]
362+ * @returns {A }
348363 */
349364 min : ( x , y , brand = undefined ) =>
350365 // eslint-disable-next-line no-nested-ternary
@@ -356,11 +371,11 @@ const AmountMath = {
356371 /**
357372 * Returns the max value between x and y using isGTE
358373 *
359- * @template {AssetKind} K
360- * @param {Amount<K> } x
361- * @param {Amount<K> } y
362- * @param {Brand<K> } [brand]
363- * @returns {Amount<K> }
374+ * @template {Amount} A
375+ * @param {A } x
376+ * @param {A } y
377+ * @param {Brand } [brand]
378+ * @returns {A }
364379 */
365380 max : ( x , y , brand = undefined ) =>
366381 // eslint-disable-next-line no-nested-ternary
@@ -376,7 +391,6 @@ harden(AmountMath);
376391const getAssetKind = amount => {
377392 assertRecord ( amount , 'amount' ) ;
378393 const { value } = amount ;
379- // @ts -expect-error cast (ignore b/c erroring in CI but not my IDE)
380394 return assertValueGetAssetKind ( value ) ;
381395} ;
382396harden ( getAssetKind ) ;
0 commit comments