Created
March 11, 2020 00:23
-
-
Save gnidan/c26a71fd541c511ef18d0ae7a0948c33 to your computer and use it in GitHub Desktop.
configurable number formats
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// dummy definitions | |
type BN = { "big-number": number }; | |
type Big = { "big": number; "dot": number }; | |
/* | |
* high-level: define a type that maps configurable things to allowable option types | |
*/ | |
// main type (extended from in generics) | |
type FormatConfig = { | |
whole: WholeNumberConfig; | |
decimal: DecimalNumberConfig; | |
}; | |
// possible configuration types | |
type WholeNumberConfig = | |
| "BN" | |
| "string"; | |
type DecimalNumberConfig = | |
| "Big" | |
| "string"; | |
// and let's just give ourselves a default | |
type DefaultFormatConfig = { | |
whole: "BN"; | |
decimal: "Big"; | |
}; | |
/* | |
* organization approach: library of fields to show opportunities for cohesion | |
* | |
* this could have major/minor order reversed, or any other scheme... | |
* but as you'll see below, the scheme doesn't matter too much (it's internal) | |
*/ | |
type WholeConfigFields = { | |
"normal": { | |
"BN": { | |
asBN: BN; | |
}; | |
"string": { | |
asString: string; | |
}; | |
}; | |
"raw": { | |
"BN": { | |
rawAsBN?: BN; | |
}; | |
"string": { | |
rawAsString?: string; | |
}; | |
} | |
} | |
type DecimalConfigFields = { | |
"normal": { | |
"Big": { | |
asBig: Big; | |
}; | |
"string": { | |
asString: string; | |
}; | |
}; | |
} | |
/* | |
* define helpers to use instead of any literal in-line type algebra | |
*/ | |
type NormalWholeFields<C extends FormatConfig> = | |
WholeConfigFields["normal"][C["whole"]]; | |
type RawWholeFields<C extends FormatConfig> = | |
WholeConfigFields["raw"][C["whole"]]; | |
type DecimalFields<C extends FormatConfig> = | |
DecimalConfigFields["normal"][C["decimal"]]; | |
/* | |
* usage example: inside type-and-value definitions | |
*/ | |
// UintValue needs to represent the normal value and the raw value... | |
// ... just include both fields! | |
interface UintValue<C extends FormatConfig = DefaultFormatConfig> { | |
type: UintType<C>; | |
kind: "value"; | |
value: NormalWholeFields<C> & RawWholeFields<C>; | |
} | |
export interface UintType<C extends FormatConfig = DefaultFormatConfig> { | |
typeClass: "uint"; | |
bits: number; | |
typeHint?: string; | |
} | |
// hypothetical getting fancy | |
interface MixedValue<C extends FormatConfig = DefaultFormatConfig> { | |
type: MixedType<C>; | |
kind: "value"; | |
// mix different configs and also fields that don't depend on configs | |
value: { "secret-ingredient": "chocolate" } // config-independent fields | |
& { wholePart: NormalWholeFields<C> & RawWholeFields<C> } // two sets of fields from one config | |
& { decimalPart: DecimalFields<C> }; // another set of fields from another config | |
} | |
export interface MixedType<C extends FormatConfig = DefaultFormatConfig> { | |
typeClass: "bag-of-tricks"; | |
} | |
// great! the two kinds of elementaries | |
type ElementaryValue<C extends FormatConfig = DefaultFormatConfig> = | |
| UintValue<C> | |
| MixedValue<C>; | |
/* | |
* usage example: actual values! | |
*/ | |
const uint: ElementaryValue<{ whole: "BN", decimal: "Big" }> = { | |
type: { | |
typeClass: "uint", | |
bits: 256 | |
}, | |
kind: "value", | |
value: { | |
asBN: { "big-number": 5 }, | |
rawAsBN: undefined | |
// fails to typecheck: | |
// asString: "5" | |
} | |
}; | |
const mixed: ElementaryValue<{ whole: "string", decimal: "Big" }> = { | |
type: { | |
typeClass: "bag-of-tricks" | |
}, | |
kind: "value", | |
value: { | |
"secret-ingredient": "chocolate", | |
wholePart: { | |
asString: "all of it", | |
rawAsString: "raw cacao is good too" | |
}, | |
decimalPart: { | |
asBig: { "big": 5, "dot": 0 } | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment