- deprecatedLatest
- v0.13.0
- v0.12.2
- v0.12.1
- v0.12.0
- v0.11.2
- v0.11.1
- v0.11.0
- v0.11.0-beta.2
- v0.11.0-beta.1
- v0.11.0-beta.0
- v0.10.2
- v0.10.1
- v0.10.0
- v0.9.1
- v0.9.0
- v0.8.2
- v0.8.1
- v0.8.0
- v0.7.1
- v0.7.0
- v0.6.2
- v0.6.1
- v0.6.0
- v0.5.3
- v0.5.2
- v0.5.1
- v0.5.0
- v0.4.2
- v0.4.1
- v0.4.0
- v0.3.3
- v0.3.2
- v0.3.1
- v0.3.0
- v0.2.1
- v0.2.0
- v0.1.4
- v0.1.3
- v0.1.2
- v0.1.1
- v0.1.0
- v0.1.0-beta.7
- v0.1.0-beta.6
- v0.1.0-beta.5
- v0.1.0-beta.4
- v0.1.0-beta.3
- v0.1.0-beta.2
- v0.1.0-beta.1
SCALE Codecs for JavaScript and TypeScript
A TypeScript implementation of SCALE (Simple Concatenated Aggregate Little-Endian) transcoding (see Rust implementation here), which emphasizes JS-land representations and e2e type-safety. These types are described below.
Setup
If youâre using Deno, simply import via the denoland/x
specifier.
import * as $ from "https://deno.land/x/scale/mod.ts";
If youâre using Node, install as follows.
npm install parity-scale-codec
Then import as follows.
import * as $ from "parity-scale-codec";
Usage
- Import the library
- Define a codec via the libraryâs functions, whose names correspond to types
- Utilize the codec youâve defined
Example
import * as $ from "https://deno.land/x/scale/mod.ts";
const $person = $.object(
["name", $.str],
["nickName", $.str],
["superPower", $.option($.str)],
);
const valueToEncode = {
name: "Magdalena",
nickName: "Magz",
superPower: "Hydrokinesis",
};
const encodedBytes: Uint8Array = $person.encode(valueToEncode);
const decodedValue: Person = $person.decode(encodedBytes);
assertEquals(decodedValue, valueToEncode);
To extract the JS-native TypeScript type from a given codec, use the Native
utility type.
type Person = $.Native<typeof $person>;
/* {
name: string;
nickName: string;
superPower: string | undefined;
} */
In cases where codecs are exceptionally large, we may want to spare the TS checker of extra work.
interface Person {
name: string;
nickName: string;
superPower: string | undefined;
}
const $person: Codec<Person> = $.object(
["name", $.str],
["nickName", $.str],
["superPower", $.option($.str)],
);
This has the added benefit of producing type errors if the codec does not directly mirror the TS type.
const $person: Codec<Person> = $.object(
// ~~~~~~~
// ^ error (message below)
["nickName", $.str],
["superPower", $.option($.str)],
);
Hovering over the error squigglies will reveal the following diagnostic.
Type 'Codec<{ nickName: string; superPower: string | undefined; }>' is not assignable to type 'Codec<Person>'.
The types returned by 'decode(...)' are incompatible between these types.
Type '{ nickName: string; superPower: string | undefined; }' is not assignable to type 'Person'.
Codec Naming
This library adopts a convention of denoting codecs with a $
â $.foo
for built-in codec, and $foo
for user-defined codecs. This makes codecs easily distinguishable from other values, and makes it easier to have codecs in scope with other variables:
interface Person { ... }
const $person = $.object(...)
const person = { ... }
Here, the type, codec, and a value can all coexist without clashing, without having to resort to wordy workarounds like personCodec
.
The main other library this could possibly clash with is jQuery, and its usage has waned enough that this is not a serious problem.
While we recommend following this convention for consistency, you can, of course, adopt an alternative convention if the $
is problematic â $.foo
can easily become s.foo
or scale.foo
with an alternate import name.
Types
Primitives
$.bool; // Codec<boolean>
$.u8; // Codec<number>
$.i8; // Codec<number>
$.u16; // Codec<number>
$.i16; // Codec<number>
$.u32; // Codec<number>
$.i32; // Codec<number>
$.u64; // Codec<bigint>
$.i64; // Codec<bigint>
$.u128; // Codec<bigint>
$.i128; // Codec<bigint>
$.u256; // Codec<bigint>
$.i256; // Codec<bigint>
// https://docs.substrate.io/v3/advanced/scale-codec/#compactgeneral-integers
$.compact; // Codec<number | bigint>
$.str; // Codec<string>
Arrays
$.sizedArray($.u8, 2); // Codec<[number, number]>
$.array($.u8); // Codec<number[]>
$.uint8array; // Codec<Uint8Array>
$.tuple($.bool, $.u8, $.str); // Codec<[boolean, number, string]>
Objects
const $person = $.object(
["name", $.str],
["nickName", $.str],
["superPower", $.option($.str)],
);
$person; /* Codec<{
name: string;
nickName: string;
superPower: string | undefined;
}> */
Iterables
$.set($.u8); // Codec<Set<number>>
$.map($.str, $.u8); // Codec<Map<string, number>>
const $manualSetU8 = $.iterable<number, Set<number>>({
$el: $.u8,
calcLength: (set) => set.size,
rehydrate: (values) => new Set(values),
});
$manualSetU8; // Codec<Set<number>>
Options
$.option($.u8); // Codec<number | undefined>
$.optionBool; // Codec<boolean | undefined> (stores as single byte; see OptionBool in Rust impl)
Unions
Explicitly Discriminated
const $strOrNum = $.union(
(value) => { // Discriminate
if (typeof value === "string") {
return 0;
} else if (typeof value === "number") {
return 1;
} else {
throw new Error("Unreachable");
}
},
$.str, // Member 0
$.u8, // Member 1
);
$strOrNum; // Codec<string | number>
Tagged Unions
const $pet = $.taggedUnion(
"_tag",
["dog", ["bark", $.str]],
["cat", ["purr", $.str]],
);
$pet; /* Codec<
| { _tag: "dog"; bark: string }
| { _tag: "cat"; purr: string }
> */
Key Literals (aka., Native TypeScript Enums)
enum Dinosaur {
Liopleurodon = "Liopleurodon",
Kosmoceratops = "Kosmoceratops",
Psittacosaurus = "Psittacosaurus",
}
const $dinosaur = $.keyLiteralUnion(
Dinosaur.Liopleurodon,
Dinosaur.Kosmoceratops,
Dinosaur.Psittacosaurus,
);
$dinosaur; // Codec<Dinosaur>
Numeric Enums
enum Dinosaur {
Liopleurodon,
Kosmoceratops,
Psittacosaurus,
}
const $dinosaur = $.u8 as Codec<Dinosaur>;
$dinosaur; // Codec<Dinosaur>
Instance
Sometimes, you may want to instantiate a class with the decoded data / encode data from a class instance. In these situations, we can leverage the instance
codec factory. A common use case for instance
codecs is Error
subtypes.
class MyError extends Error {
constructor(
readonly code: number,
readonly message: string,
readonly payload: {
a: string;
b: number;
c: boolean;
},
) {
super();
}
}
const $myError = $.instance(
MyError,
["code", $.u8],
["message", $.str],
[
"payload",
$.object(
["a", $.str],
["b", $.u8],
["c", $.bool],
),
],
);
$myError; // Codec<MyError>
Results
Result
s are initialized with an Ok
codec and an Error
instance codec.
class MyError {
constructor(readonly message: string) {}
}
const $myError = $.instance(MyError, ["message", $.str]);
const $myResult = $.result($.str, $myError);
$myResult; // Codec<string | MyError>