Skip to content

Instantly share code, notes, and snippets.

@gnidan
Created March 11, 2020 00:23
Show Gist options
  • Save gnidan/c26a71fd541c511ef18d0ae7a0948c33 to your computer and use it in GitHub Desktop.
Save gnidan/c26a71fd541c511ef18d0ae7a0948c33 to your computer and use it in GitHub Desktop.
configurable number formats
// 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