diff --git a/packages/api-observable/src/classes.ts b/packages/api-observable/src/classes.ts index 568e9dad395a..5111438c322a 100644 --- a/packages/api-observable/src/classes.ts +++ b/packages/api-observable/src/classes.ts @@ -16,15 +16,15 @@ export class RxProposal extends Struct.with({ id: PropIndex, proposal: Proposal, } get address (): AccountId { - return this.raw.address as AccountId; + return this.get('address') as AccountId; } get id (): PropIndex { - return this.raw.id as PropIndex; + return this.get('id') as PropIndex; } get proposal (): Proposal { - return this.raw.proposal as Proposal; + return this.get('proposal') as Proposal; } } @@ -37,11 +37,11 @@ export class RxProposalDeposits extends Struct.with({ balance: Balance, addresse } get addresses (): Vector { - return this.raw.addresses as Vector; + return this.get('addresses') as Vector; } get balance (): Balance { - return this.raw.balance as Balance; + return this.get('balance') as Balance; } } @@ -56,18 +56,18 @@ export class RxReferendum extends Struct.with({ blockNumber: BlockNumber, propos } get blockNumber (): BlockNumber { - return this.raw.blockNumber as BlockNumber; + return this.get('blockNumber') as BlockNumber; } get id (): ReferendumIndex { - return this.raw.id as ReferendumIndex; + return this.get('id') as ReferendumIndex; } get proposal (): Proposal { - return this.raw.proposal as Proposal; + return this.get('proposal') as Proposal; } get voteThreshold (): VoteThreshold { - return this.raw.voteThreshold as VoteThreshold; + return this.get('voteThreshold') as VoteThreshold; } } diff --git a/packages/types/src/Bft.ts b/packages/types/src/Bft.ts index 0bafa9305db8..77f376ea05fa 100644 --- a/packages/types/src/Bft.ts +++ b/packages/types/src/Bft.ts @@ -28,11 +28,11 @@ export class BftAuthoritySignature extends Tuple { } get authorityId (): AuthorityId { - return this.raw.authorityId as AuthorityId; + return this.getAtIndex(0) as AuthorityId; } get signature (): Signature { - return this.raw.signature as Signature; + return this.getAtIndex(1) as Signature; } } @@ -52,11 +52,11 @@ export class BftHashSignature extends Tuple { } get hash (): Hash { - return this.raw.hash as Hash; + return this.getAtIndex(0) as Hash; } get signature (): Signature { - return this.raw.signature as Signature; + return this.getAtIndex(1) as Signature; } } @@ -78,14 +78,14 @@ export class Justification extends Struct { } get hash (): Hash { - return this.raw.hash as Hash; + return this.get('hash') as Hash; } get round (): U32 { - return this.raw.round as U32; + return this.get('round') as U32; } get signatures (): Vector { - return this.raw.signatures as Vector; + return this.get('signatures') as Vector; } } diff --git a/packages/types/src/Block.ts b/packages/types/src/Block.ts index 99a30f680b2f..3535435c1507 100644 --- a/packages/types/src/Block.ts +++ b/packages/types/src/Block.ts @@ -26,7 +26,7 @@ export default class Block extends Struct { } get extrinsics (): Extrinsics { - return this.raw.extrinsics as Extrinsics; + return this.get('extrinsics') as Extrinsics; } // convenience, encodes the header and returns the actual hash @@ -37,6 +37,6 @@ export default class Block extends Struct { } get header (): Header { - return this.raw.header as Header; + return this.get('header') as Header; } } diff --git a/packages/types/src/Extrinsic.ts b/packages/types/src/Extrinsic.ts index b8656cd5e97c..5a08f885f39b 100644 --- a/packages/types/src/Extrinsic.ts +++ b/packages/types/src/Extrinsic.ts @@ -94,11 +94,11 @@ export default class Extrinsic extends Struct { } get method (): Method { - return this.raw.method as Method; + return this.get('method') as Method; } get signature (): ExtrinsicSignature { - return this.raw.signature as ExtrinsicSignature; + return this.get('signature') as ExtrinsicSignature; } get encodedLength (): number { diff --git a/packages/types/src/ExtrinsicSignature.ts b/packages/types/src/ExtrinsicSignature.ts index 92d91c491fe1..6549b7441a67 100644 --- a/packages/types/src/ExtrinsicSignature.ts +++ b/packages/types/src/ExtrinsicSignature.ts @@ -74,19 +74,19 @@ export default class ExtrinsicSignature extends Struct { } get era (): ExtrinsicEra { - return this.raw.era as ExtrinsicEra; + return this.get('era') as ExtrinsicEra; } get nonce (): Nonce { - return this.raw.nonce as Nonce; + return this.get('nonce') as Nonce; } get signature (): Signature { - return this.raw.signature as Signature; + return this.get('signature') as Signature; } get signer (): Address { - return this.raw.signer as Address; + return this.get('signer') as Address; } get version (): number { @@ -112,10 +112,10 @@ export default class ExtrinsicSignature extends Struct { }); const signature = new Signature(signingPayload.sign(signerPair)); - this.raw.era = signingPayload.era; - this.raw.nonce = signingPayload.nonce; - this.raw.signer = signer; - this.raw.signature = signature; + this.set('era', signingPayload.era); + this.set('nonce', signingPayload.nonce); + this.set('signer', signer); + this.set('signature', signature); return this; } diff --git a/packages/types/src/Header.ts b/packages/types/src/Header.ts index 7a0c1a965675..811097780ae4 100644 --- a/packages/types/src/Header.ts +++ b/packages/types/src/Header.ts @@ -36,7 +36,7 @@ export class Digest extends Struct { } get logs (): Vector { - return this.raw.logs as Vector; + return this.get('logs') as Vector; } } @@ -53,15 +53,15 @@ export default class Header extends Struct { } get blockNumber (): BlockNumber { - return this.raw.number as BlockNumber; + return this.get('number') as BlockNumber; } get digest (): Digest { - return this.raw.digest as Digest; + return this.get('digest') as Digest; } get extrinsicsRoot (): Hash { - return this.raw.extrinsicsRoot as Hash; + return this.get('extrinsicsRoot') as Hash; } // convenience, encodes the header and returns the actual hash @@ -72,10 +72,10 @@ export default class Header extends Struct { } get parentHash (): Hash { - return this.raw.parentHash as Hash; + return this.get('parentHash') as Hash; } get stateRoot (): Hash { - return this.raw.stateRoot as Hash; + return this.get('stateRoot') as Hash; } } diff --git a/packages/types/src/KeyValue.ts b/packages/types/src/KeyValue.ts index a911d63ba5ee..e7f777af4003 100644 --- a/packages/types/src/KeyValue.ts +++ b/packages/types/src/KeyValue.ts @@ -28,11 +28,11 @@ export default class KeyValue extends Struct { } get key (): StorageKey { - return this.raw.key as StorageKey; + return this.get('key') as StorageKey; } get value (): StorageData { - return this.raw.value as StorageData; + return this.get('value') as StorageData; } } @@ -53,10 +53,10 @@ export class KeyValueOption extends Tuple { } get key (): StorageKey { - return this.raw.key as StorageKey; + return this.getAtIndex(0) as StorageKey; } get value (): Option { - return this.raw.value as Option; + return this.getAtIndex(1) as Option; } } diff --git a/packages/types/src/Metadata.ts b/packages/types/src/Metadata.ts index 845b1a14f8cf..a9cdb8dadb14 100644 --- a/packages/types/src/Metadata.ts +++ b/packages/types/src/Metadata.ts @@ -32,15 +32,15 @@ export class OuterDispatchCall extends Struct { } get index (): U16 { - return this.raw.index as U16; + return this.get('index') as U16; } get name (): Text { - return this.raw.name as Text; + return this.get('name') as Text; } get prefix (): Text { - return this.raw.prefix as Text; + return this.get('prefix') as Text; } } @@ -53,11 +53,11 @@ export class OuterDispatchMetadata extends Struct { } get calls (): Vector { - return this.raw.calls as Vector; + return this.get('calls') as Vector; } get name (): Text { - return this.raw.name as Text; + return this.get('name') as Text; } } @@ -71,15 +71,15 @@ export class EventMetadata extends Struct { } get arguments (): Vector { - return this.raw.arguments as Vector; + return this.get('arguments') as Vector; } get documentation (): Vector { - return this.raw.documentation as Vector; + return this.get('documentation') as Vector; } get name (): Text { - return this.raw.name as Text; + return this.get('name') as Text; } } @@ -109,11 +109,11 @@ export class OuterEventMetadata extends Struct { } get events (): Vector { - return this.raw.events as Vector; + return this.get('events') as Vector; } get name (): Text { - return this.raw.name as Text; + return this.get('name') as Text; } } @@ -126,11 +126,11 @@ export class FunctionArgumentMetadata extends Struct { } get name (): Text { - return this.raw.name as Text; + return this.get('name') as Text; } get type (): Type { - return this.raw.type as Type; + return this.get('type') as Type; } } @@ -145,19 +145,19 @@ export class FunctionMetadata extends Struct { } get arguments (): Vector { - return this.raw.arguments as Vector; + return this.get('arguments') as Vector; } get documentation (): Vector { - return this.raw.documentation as Vector; + return this.get('documentation') as Vector; } get id (): U16 { - return this.raw.id as U16; + return this.get('id') as U16; } get name (): Text { - return this.raw.name as Text; + return this.get('name') as Text; } } @@ -170,11 +170,11 @@ export class CallMetadata extends Struct { } get functions (): Vector { - return this.raw.functions as Vector; + return this.get('functions') as Vector; } get name (): Text { - return this.raw.name as Text; + return this.get('name') as Text; } } @@ -187,11 +187,11 @@ export class ModuleMetadata extends Struct { } get call (): CallMetadata { - return this.raw.call as CallMetadata; + return this.get('call') as CallMetadata; } get name (): Text { - return this.raw.name as Text; + return this.get('name') as Text; } } @@ -210,11 +210,11 @@ export class StorageFunctionType$Map extends Struct { } get key (): Type { - return this.raw.type as Type; + return this.get('key') as Type; } get value (): Type { - return this.raw.value as Type; + return this.get('value') as Type; } } @@ -263,19 +263,19 @@ export class StorageFunctionMetadata extends Struct { } get documentation (): Vector { - return this.raw.documentation as Vector; + return this.get('documentation') as Vector; } get name (): Text { - return this.raw.name as Text; + return this.get('name') as Text; } get modifier (): StorageFunctionModifier { - return this.raw.modifier as StorageFunctionModifier; + return this.get('modifier') as StorageFunctionModifier; } get type (): StorageFunctionType { - return this.raw.type as StorageFunctionType; + return this.get('type') as StorageFunctionType; } } @@ -288,11 +288,11 @@ export class StorageMetadata extends Struct { } get functions (): Vector { - return this.raw.functions as Vector; + return this.get('functions') as Vector; } get prefix (): Text { - return this.raw.prefix as Text; + return this.get('prefix') as Text; } } @@ -306,15 +306,15 @@ export class RuntimeModuleMetadata extends Struct { } get module (): ModuleMetadata { - return this.raw.module as ModuleMetadata; + return this.get('module') as ModuleMetadata; } get prefix (): Text { - return this.raw.prefix as Text; + return this.get('prefix') as Text; } get storage (): StorageMetadata | undefined { - return (this.raw.storage as Option).value; + return (this.get('storage') as Option).value; } } @@ -358,15 +358,15 @@ export default class RuntimeMetadata extends Struct { } get calls (): Vector { - return (this.raw.outerDispatch as OuterDispatchMetadata).calls; + return (this.get('outerDispatch') as OuterDispatchMetadata).calls; } get events (): Vector { - return (this.raw.outerEvent as OuterEventMetadata).events; + return (this.get('outerEvent') as OuterEventMetadata).events; } get modules (): Vector { - return this.raw.modules as Vector; + return this.get('modules') as Vector; } // Helper to retrieve a list of all type that are found, sorted and de-deuplicated diff --git a/packages/types/src/Method.ts b/packages/types/src/Method.ts index 6ec31cf3053e..daab2a259c40 100644 --- a/packages/types/src/Method.ts +++ b/packages/types/src/Method.ts @@ -147,7 +147,8 @@ export default class Method extends Struct { } get args (): Array { - return (this.getAtIndex(1) as Struct).values(); + // FIXME This should return a Struct instead of an Array + return [...(this.get('args') as Struct).values()]; } get argsDef (): ArgsDef { @@ -155,11 +156,11 @@ export default class Method extends Struct { } get callIndex (): Uint8Array { - return (this.getAtIndex(0) as MethodIndex).callIndex; + return (this.get('methodIndex') as MethodIndex).callIndex; } get data (): Uint8Array { - return (this.getAtIndex(1) as Struct).toU8a(); + return (this.get('args') as Struct).toU8a(); } get meta (): FunctionMetadata { diff --git a/packages/types/src/MisbehaviorReport.ts b/packages/types/src/MisbehaviorReport.ts index c1446538655c..aedcf1030217 100644 --- a/packages/types/src/MisbehaviorReport.ts +++ b/packages/types/src/MisbehaviorReport.ts @@ -34,15 +34,15 @@ export class BftAtReport extends Struct { } get a (): BftHashSignature { - return this.raw.a as BftHashSignature; + return this.get('a') as BftHashSignature; } get b (): BftHashSignature { - return this.raw.b as BftHashSignature; + return this.get('b') as BftHashSignature; } get round (): U32 { - return this.raw.round as U32; + return this.get('round') as U32; } } @@ -80,18 +80,18 @@ export default class MisbehaviorReport extends Struct { } get misbehavior (): MisbehaviorKind { - return this.raw.misbehavior as MisbehaviorKind; + return this.get('misbehavior') as MisbehaviorKind; } get parentHash (): Hash { - return this.raw.parentHash as Hash; + return this.get('parentHash') as Hash; } get parentNumber (): BlockNumber { - return this.raw.parentNumber as BlockNumber; + return this.get('parentNumber') as BlockNumber; } get target (): AuthorityId { - return this.raw.target as AuthorityId; + return this.get('target') as AuthorityId; } } diff --git a/packages/types/src/RuntimeVersion.ts b/packages/types/src/RuntimeVersion.ts index 6d57f90933b0..2cf38745119c 100644 --- a/packages/types/src/RuntimeVersion.ts +++ b/packages/types/src/RuntimeVersion.ts @@ -31,11 +31,11 @@ class RuntimeVersionApi extends Tuple { } get id (): ApiId { - return this.raw.id as ApiId; + return this.getAtIndex(0) as ApiId; } get version (): U32 { - return this.raw.version as U32; + return this.getAtIndex(1) as U32; } } @@ -67,26 +67,26 @@ export default class RuntimeVersion extends Struct { } get apis (): Vector { - return this.raw.apis as Vector; + return this.get('apis') as Vector; } get authoringVersion (): U32 { - return this.raw.authoringVersion as U32; + return this.get('authoringVersion') as U32; } get implName (): Text { - return this.raw.implName as Text; + return this.get('implName') as Text; } get implVersion (): U32 { - return this.raw.implVersion as U32; + return this.get('implVersion') as U32; } get specName (): Text { - return this.raw.specName as Text; + return this.get('specName') as Text; } get specVersion (): U32 { - return this.raw.specVersion as U32; + return this.get('specVersion') as U32; } } diff --git a/packages/types/src/SignaturePayload.ts b/packages/types/src/SignaturePayload.ts index 4d623b7b4af8..39052ea0efc9 100644 --- a/packages/types/src/SignaturePayload.ts +++ b/packages/types/src/SignaturePayload.ts @@ -40,19 +40,19 @@ export default class SignaturePayload extends Struct { } get blockHash (): Hash { - return this.raw.blockHash as Hash; + return this.get('blockHash') as Hash; } get method (): Method { - return this.raw.method as Method; + return this.get('method') as Method; } get era (): ExtrinsicEra { - return this.raw.era as ExtrinsicEra; + return this.get('era') as ExtrinsicEra; } get nonce (): Nonce { - return this.raw.nonce as Nonce; + return this.get('nonce') as Nonce; } get signature (): Uint8Array { diff --git a/packages/types/src/SignedBlock.ts b/packages/types/src/SignedBlock.ts index 8809b274f67f..3a5d890d9d13 100644 --- a/packages/types/src/SignedBlock.ts +++ b/packages/types/src/SignedBlock.ts @@ -20,10 +20,10 @@ export default class SignedBlock extends Struct { } get block (): Block { - return this.raw.block as Block; + return this.get('block') as Block; } get justification (): Justification { - return this.raw.justification as Justification; + return this.get('justification') as Justification; } } diff --git a/packages/types/src/StorageChangeSet.ts b/packages/types/src/StorageChangeSet.ts index 7ed8fb26e68b..c01f6e3000b9 100644 --- a/packages/types/src/StorageChangeSet.ts +++ b/packages/types/src/StorageChangeSet.ts @@ -25,10 +25,10 @@ export default class StorageChangeSet extends Struct { } get changes (): Vector { - return this.raw.changes as Vector; + return this.get('changes') as Vector; } get hash (): Hash { - return this.raw.hash as Hash; + return this.get('hash') as Hash; } } diff --git a/packages/types/src/ValidatorPrefs.ts b/packages/types/src/ValidatorPrefs.ts index f22c508cf27b..867e904f513c 100644 --- a/packages/types/src/ValidatorPrefs.ts +++ b/packages/types/src/ValidatorPrefs.ts @@ -22,10 +22,10 @@ export default class ValidatorPrefs extends Struct { } get unstakeThreshold (): U32 { - return this.raw.unstakeThreshold as U32; + return this.get('unstakeThreshold') as U32; } get validatorPayment (): Balance { - return this.raw.validatorPayment as Balance; + return this.get('validatorPayment') as Balance; } } diff --git a/packages/types/src/codec/Struct.spec.js b/packages/types/src/codec/Struct.spec.ts similarity index 51% rename from packages/types/src/codec/Struct.spec.js rename to packages/types/src/codec/Struct.spec.ts index 4cb43b7f71bd..9ff1271b03d7 100644 --- a/packages/types/src/codec/Struct.spec.js +++ b/packages/types/src/codec/Struct.spec.ts @@ -2,26 +2,27 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. +import { CodecTo } from '../types'; import Struct from './Struct'; import Text from '../Text'; import U32 from '../U32'; import Vector from './Vector'; -const testDecode = (type, input) => +const testDecode = (type: string, input: any) => it(`can decode from ${type}`, () => { const s = new Struct({ foo: Text, bar: U32 }, input); - expect(s.keys()).toEqual(['foo', 'bar']); + expect([...s.keys()]).toEqual(['foo', 'bar']); expect( - s.values().map((v) => + [...s.values()].map((v) => v.toString() ) ).toEqual(['bazzing', '69']); }); -const testEncode = (to, expected) => +const testEncode = (to: CodecTo, expected: any) => it(`can encode ${to}`, () => { const s = new Struct({ foo: Text, @@ -32,13 +33,26 @@ const testEncode = (to, expected) => describe('Struct', () => { + testDecode('array', ['bazzing', 69]); + testDecode('hex', '0x1c62617a7a696e6745000000'); testDecode('object', { foo: 'bazzing', bar: 69 }); testDecode('Uint8Array', Uint8Array.from([28, 98, 97, 122, 122, 105, 110, 103, 69, 0, 0, 0])); - // testEncode('toHex', '0x1c62617a7a696e6745000000'); // FIXME Add this + testEncode('toHex', '0x1c62617a7a696e6745000000'); + testEncode('toJSON', { foo: 'bazzing', bar: 69 }); testEncode('toU8a', Uint8Array.from([28, 98, 97, 122, 122, 105, 110, 103, 69, 0, 0, 0])); - testEncode('toString', '{foo: bazzing, bar: 69}'); + testEncode('toString', '{"foo":"bazzing","bar":69}'); + it('decodes null', () => { + expect( + new ( + Struct.with({ + txt: Text, + u32: U32 + }) + )(null).toString() + ).toEqual('{}'); + }); it('decodes a more complicated type', () => { const s = new Struct({ @@ -46,8 +60,19 @@ describe('Struct', () => { bar: Text })) }, { foo: [{ bar: 1 }, { bar: 2 }] }); - expect(s.toString()).toBe('{foo: [{bar: 1}, {bar: 2}]}'); - }) + expect(s.toString()).toBe('{"foo":[{"bar":"1"},{"bar":"2"}]}'); + }); + + it('throws when it cannot decode', () => { + expect( + () => new ( + Struct.with({ + txt: Text, + u32: U32 + }) + )('ABC') + ).toThrowError(/Struct: cannot decode type/); + }); it('provides a clean toString()', () => { expect( @@ -57,7 +82,18 @@ describe('Struct', () => { u32: U32 }) )({ txt: 'foo', u32: 0x123456 }).toString() - ).toEqual(`{txt: foo, u32: 1193046}`); + ).toEqual('{"txt":"foo","u32":1193046}'); + }); + + it('correctly encodes length', () => { + expect( + new ( + Struct.with({ + txt: Text, + u32: U32 + }) + )({ foo: 'bazzing', bar: 69 }).encodedLength + ).toEqual(5); }); it('exposes the types', () => { @@ -66,6 +102,10 @@ describe('Struct', () => { foo: Text, bar: Text, baz: U32 + }, { + foo: 'foo', + bar: 'bar', + baz: 3 }).Type ).toEqual({ foo: 'Text', @@ -74,17 +114,16 @@ describe('Struct', () => { }); }); - it('exposes the keys/values', () => { - const test = new Struct({ - foo: Text, - bar: U32 - }, { foo: 'bazzing', bar: 69 }); - - expect(test.keys()).toEqual(['foo', 'bar']); + it('gets the value at a particular index', () => { expect( - test.values().map((v) => - v.toString() - ) - ).toEqual(['bazzing', '69']); + new ( + Struct.with({ + txt: Text, + u32: U32 + }) + )({ txt: 'foo', u32: 1234 }) + .getAtIndex(1) + .toString() + ).toEqual('1234'); }); }); diff --git a/packages/types/src/codec/Struct.ts b/packages/types/src/codec/Struct.ts index 2e6c8f14640b..3f2b1853573a 100644 --- a/packages/types/src/codec/Struct.ts +++ b/packages/types/src/codec/Struct.ts @@ -2,10 +2,10 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. -import { hexToU8a, isHex, isObject, isU8a, u8aConcat } from '@polkadot/util'; +import { hexToU8a, isHex, isObject, isU8a, u8aConcat, u8aToHex } from '@polkadot/util'; import Base from './Base'; -import { Constructor } from '../types'; +import { Codec, Constructor, ConstructorDef } from '../types'; // A Struct defines an Object with key/values - where the values are Base values. It removes // a lot of repetition from the actual coding, define a structure type, pass it the key/Base @@ -14,34 +14,57 @@ import { Constructor } from '../types'; // it needs to decoded in the specific defined order. export default class Struct< // The actual Class structure, i.e. key -> Class - S = { [index: string]: Constructor }, + S extends ConstructorDef = ConstructorDef, // internal type, instance of classes mapped by key - T = { [K in keyof S]: Base }, + T extends { [K in keyof S]: Base } = { [K in keyof S]: Base }, // input values, mapped by key can be anything (construction) - V = { [K in keyof S]: any }, + V extends { [K in keyof S]: any } = { [K in keyof S]: any }, // type names, mapped by key, name of Class in S - E = { [K in keyof S]: string } - > extends Base { + E extends { [K in keyof S]: string } = { [K in keyof S]: string } + > extends Map implements Codec { + public raw: Map; // FIXME Remove this once we convert all types out of Base protected _jsonMap: Map; protected _Types: E; constructor (Types: S, value: V | Array = {} as V, jsonMap: Map = new Map()) { + const decoded = Struct.decodeStruct(Types, value, jsonMap); super( - Struct.decodeStruct(Types, value, jsonMap) + Object.entries(decoded) ); this._jsonMap = jsonMap; - this._Types = Object - .keys(Types) + this._Types = (Object + .keys(Types) as Array) .reduce((result: E, key) => { - // @ts-ignore Same as above, can't do a simple one, I'm missing something simple result[key] = Types[key].name; return result; }, {} as E); + + // FIXME Remove this once we convert all types out of Base + this.raw = this; } - static decodeStruct (Types: S, value: any, jsonMap: Map): T { + /** + * Decode input to pass into constructor. + * + * @param Types - Types definition. + * @param value - Value to decode, one of: + * - null + * - undefined + * - hex + * - Uint8Array + * - object with `{ key1: value1, key2: value2 }`, assuming `key1` and `key2` + * are also keys in `Types` + * - array with `[value1, value2]` assuming the array has the same length as + * `Object.keys(Types)` + * @param jsonMap + */ + private static decodeStruct< + S extends ConstructorDef, + _, + T extends { [K in keyof S]: Base } + > (Types: S, value: any, jsonMap: Map): T { // l.debug(() => ['Struct.decode', { Types, value }]); if (isHex(value)) { @@ -54,15 +77,14 @@ export default class Struct< // used to track at which index we are currently parsing in that array. let currentIndex = 0; - return Object - .keys(Types) + return (Object + .keys(Types) as Array) .reduce((raw: T, key, index) => { // The key in the JSON can be snake_case (or other cases), but in our // Types, result or any other maps, it's camelCase const jsonKey = (jsonMap.get(key as any) && !value[key]) ? jsonMap.get(key as any) : key; if (isU8a(value)) { - // @ts-ignore FIXME See below raw[key] = new Types[key]( value.subarray( currentIndex @@ -70,30 +92,17 @@ export default class Struct< ); // Move the currentIndex forward - // @ts-ignore FIXME See below currentIndex += raw[key].encodedLength; - // @ts-ignore FIXME See below - } else if (value[jsonKey] instanceof Types[key]) { - // @ts-ignore FIXME See below - raw[key] = value[jsonKey]; - } else if (Array.isArray(value) && value.length === Object.keys(Types).length) { - // @ts-ignore FIXME See below - raw[key] = new Types[key]( - // @ts-ignore FIXME - value[index] - ); + } else if (Array.isArray(value)) { + raw[key] = value[index] instanceof Types[key] + ? value[index] + : new Types[key](value[index]); } else if (isObject(value)) { - // @ts-ignore FIXME Ok, something weird is going on here or I just don't get it... - // it works, so ignore the checker, although it drives me batty. (It started when - // the [key in keyof T] was added, the idea is to provide better checks, which - // does backfire here, but works externally.) - raw[key] = new Types[key]( - // @ts-ignore FIXME - value[jsonKey] - ); + raw[key] = value[jsonKey as string] instanceof Types[key] + ? value[jsonKey as string] + : new Types[key](value[jsonKey as string]); } else { - // @ts-ignore FIXME - throw new Error(`Struct: cannot decode type "${Types[key].name}" with value "${value}".`); + throw new Error(`Struct: cannot decode type "${Types[key].name}" with value "${JSON.stringify(value)}".`); } return raw; @@ -101,7 +110,7 @@ export default class Struct< } static with< - S = { [index: string]: Constructor } + S extends ConstructorDef > (Types: S): Constructor> { return class extends Struct { constructor (value?: any, jsonMap?: Map) { @@ -115,48 +124,39 @@ export default class Struct< } get encodedLength (): number { - return Object.values(this.raw).reduce((length, entry) => { + return [...this.values()].reduce((length, entry) => { return length += entry.encodedLength; }, 0); } getAtIndex (index: number): Base { - return this.values()[index]; + return [...this.values()][index]; + } + + toHex () { + return u8aToHex(this.toU8a()); } toJSON (): any { - return Object.keys(this.raw).reduce((json, key) => { - const jsonKey = this._jsonMap.get(key as any) || key; + return [...this.keys()].reduce((json, key) => { + const jsonKey = this._jsonMap.get(key) || key; - // @ts-ignore as above... - json[jsonKey] = this.raw[key].toJSON(); + const value = this.get(key); + json[jsonKey] = value && value.toJSON(); return json; }, {} as any); } - keys (): Array { - return Object.keys(this.raw); + toString () { + return JSON.stringify(this.toJSON()); } toU8a (isBare?: boolean): Uint8Array { return u8aConcat( - ...Object.values(this.raw).map((entry) => + ...[...this.values()].map((entry) => entry.toU8a(isBare) ) ); } - - toString (): string { - const data = Object.keys(this.raw).map((key) => - // @ts-ignore as above... - `${key}: ${this.raw[key].toString()}` - ).join(', '); - - return `{${data}}`; - } - - values (): Array { - return Object.values(this.raw); - } } diff --git a/packages/types/src/codec/Tuple.ts b/packages/types/src/codec/Tuple.ts index 740afb9f2a71..51e59ea2556c 100644 --- a/packages/types/src/codec/Tuple.ts +++ b/packages/types/src/codec/Tuple.ts @@ -2,7 +2,7 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. -import { AnyU8a, Constructor } from '../types'; +import { Constructor, ConstructorDef } from '../types'; import Base from './Base'; import Struct from './Struct'; @@ -11,32 +11,12 @@ import Struct from './Struct'; // while the U8a encoding is handled in the same way as a Struct export default class Tuple< // S & T definitions maps to what we have in Struct (naming documented there) - S = { [index: string]: Constructor }, - T = { [K in keyof S]: Base }, - V = { [K in keyof S]: any } + S extends ConstructorDef = { [index: string]: Constructor }, + T extends { [K in keyof S]: Base }= { [K in keyof S]: Base }, + V extends { [K in keyof S]: any } = { [K in keyof S]: any } > extends Struct { - constructor (Types: S, value?: V | AnyU8a, jsonMap?: Map) { - super(Types, Tuple.decodeTuple(Types, value), jsonMap); - } - - static decodeTuple (Types: S, value: V | AnyU8a): V { - // If the input is an array, we convert it to a map - if (Array.isArray(value)) { - return Object - .keys(Types) - .reduce((result, key, index) => { - // @ts-ignore FIXME these types are a headache - result[key] = value[index]; - return result; - }, {} as V); - } - - // Or else, just decode like a normal Struct - return value as V; - } - static with< - S = { [index: string]: Constructor } + S extends ConstructorDef= { [index: string]: Constructor } > (Types: S): Constructor> { return class extends Tuple { constructor (value?: any) { @@ -46,7 +26,7 @@ export default class Tuple< } toJSON (): any { - return Object.values(this.raw).map((entry) => + return [...this.values()].map((entry) => entry.toJSON() ); } diff --git a/packages/types/src/codec/createType.ts b/packages/types/src/codec/createType.ts index 2e1c52458c9b..1444de726fab 100644 --- a/packages/types/src/codec/createType.ts +++ b/packages/types/src/codec/createType.ts @@ -4,13 +4,12 @@ import { assert } from '@polkadot/util'; +import { Constructor } from '../types'; import Text from '../Text'; import Base from './Base'; import Tuple from './Tuple'; import Vector from './Vector'; -type Constructor = { new (value?: any): Base }; - export enum TypeDefInfo { Plain, Tuple, diff --git a/packages/types/src/types.d.ts b/packages/types/src/types.d.ts index 5df0e807db6d..ec046177d4a5 100644 --- a/packages/types/src/types.d.ts +++ b/packages/types/src/types.d.ts @@ -5,12 +5,27 @@ import BN from 'bn.js'; import * as Classes from './index'; +import Base from './codec/Base'; import { U8a, UInt } from './codec'; export type AnyNumber = UInt | BN | Uint8Array | number | string; export type AnyU8a = U8a | Uint8Array | Array | string; +export interface Codec { + encodedLength: number; + toHex(): string + toJSON(): any; + toString(): string; + toU8a(isBare?: boolean): Uint8Array; +} + +export type CodecTo = 'toHex' | 'toJSON' | 'toString' | 'toU8a' + export type CodecTypes = keyof typeof Classes; -export type Constructor = { new(value?: any): T }; +export type Constructor = { new(value?: any): T }; + +export type ConstructorDef = { [index: string]: Constructor }; + +export type TypeDef = { [index: string]: Base };