Allow for (ab)use of Option<Null>/Option<()> as a boolean (#4639)

* Allow for (ab)use of Option<Null> as a boolean

* Additional test for ()

* Adjust
This commit is contained in:
Jaco
2022-03-08 12:58:33 +02:00
committed by GitHub
parent e4cae64187
commit 0b03f73975
3 changed files with 31 additions and 7 deletions
+15 -1
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import { TypeRegistry } from '@polkadot/types';
import { bool, Bytes, Option, Text, U32 } from '@polkadot/types-codec';
import { bool, Bytes, Null, Option, Text, U32 } from '@polkadot/types-codec';
const registry = new TypeRegistry();
@@ -27,6 +27,20 @@ describe('Option', (): void => {
expect(new Option(registry, Text, null).isNone).toBe(true);
expect(new Option(registry, Text, 'test').isNone).toBe(false);
expect(new Option(registry, Text, '0x').isNone).toBe(true);
expect(new Option(registry, '()', null).isNone).toBe(true);
});
it('can wrap an Option<Null>/Option<()>', (): void => {
[
new Option(registry, Null, new Null(registry)),
new Option(registry, '()', new Null(registry))
].forEach((test): void => {
expect(test.isSome).toBe(true);
expect(test.isNone).toBe(false);
expect(test.isEmpty).toBe(false);
expect(test.toU8a()).toEqual(new Uint8Array([1]));
expect(test.unwrap().toHex()).toEqual('0x');
});
});
it('correctly handles booleans', (): void => {
+15 -6
View File
@@ -9,6 +9,15 @@ import { assert, isCodec, isNull, isU8a, isUndefined, u8aToHex } from '@polkadot
import { typeToConstructor } from '../utils';
import { Null } from './Null';
class None extends Null {
/**
* @description Returns the base runtime type name for this instance
*/
public override toRawType (): string {
return 'None';
}
}
/** @internal */
function decodeOption (registry: Registry, Type: CodecClass, value?: unknown): Codec {
// In the case of an option, unwrap the inner
@@ -16,8 +25,8 @@ function decodeOption (registry: Registry, Type: CodecClass, value?: unknown): C
value = value.value;
}
if (isNull(value) || isUndefined(value) || value instanceof Null || value === '0x') {
return new Null(registry);
if (isNull(value) || isUndefined(value) || value === '0x') {
return new None(registry);
} else if (value instanceof Type) {
// don't re-create, use as it (which also caters for derived types)
return value;
@@ -25,7 +34,7 @@ function decodeOption (registry: Registry, Type: CodecClass, value?: unknown): C
// the isU8a check happens last in the if-tree - since the wrapped value
// may be an instance of it, so Type and Option checks go in first
return !value.length || value[0] === 0
? new Null(registry)
? new None(registry)
: new Type(registry, value.subarray(1));
}
@@ -55,7 +64,7 @@ export class Option<T extends Codec> implements IOption<T> {
const Type = typeToConstructor(registry, typeName);
const decoded = isU8a(value) && value.length && !isCodec(value)
? value[0] === 0
? new Null(registry)
? new None(registry)
: new Type(registry, value.subarray(1))
: decodeOption(registry, Type, value);
@@ -63,7 +72,7 @@ export class Option<T extends Codec> implements IOption<T> {
this.#Type = Type;
this.#raw = decoded as T;
if (decoded.initialU8aLength) {
if (decoded && decoded.initialU8aLength) {
this.#initialU8aLength = 1 + decoded.initialU8aLength;
}
}
@@ -109,7 +118,7 @@ export class Option<T extends Codec> implements IOption<T> {
* @description Checks if the Option has no value
*/
public get isNone (): boolean {
return this.#raw instanceof Null;
return this.#raw instanceof None;
}
/**