diff --git a/packages/candid/src/idl.test.ts b/packages/candid/src/idl.test.ts index 7ab98b6b0..284012c5b 100644 --- a/packages/candid/src/idl.test.ts +++ b/packages/candid/src/idl.test.ts @@ -180,7 +180,9 @@ test('IDL encoding (arraybuffer)', () => { IDL.encode([IDL.Vec(IDL.Nat8)], [new Uint8Array()]); IDL.encode([IDL.Vec(IDL.Nat8)], [new Uint8Array(100).fill(42)]); IDL.encode([IDL.Vec(IDL.Nat16)], [new Uint16Array(200).fill(42)]); - expect(() => IDL.encode([IDL.Vec(IDL.Int8)], [new Uint16Array(10).fill(420)])).toThrow(/Invalid vec int8 argument/); + expect(() => IDL.encode([IDL.Vec(IDL.Int8)], [new Uint16Array(10).fill(420)])).toThrow( + /Invalid vec int8 argument/, + ); }); test('IDL encoding (array)', () => { @@ -677,3 +679,16 @@ test('should decode matching optional fields if wire type contains additional fi b: ['123'], }); }); + +test('should coerce value to opt value', () => { + const encoded = IDL.encode([IDL.Text], ['hello']); + const value = IDL.decode([IDL.Opt(IDL.Text)], encoded)[0] as [string]; + expect(value).toEqual(['hello']); +}); + +test('should not coerce nested opt opt', () => { + const encoded = IDL.encode([IDL.Text], ['hello']); + expect(() => IDL.decode([IDL.Opt(IDL.Opt(IDL.Text))], encoded)).toThrow( + 'type mismatch: type on the wire text, expect type opt opt text', + ); +}); diff --git a/packages/candid/src/idl.ts b/packages/candid/src/idl.ts index 4b246da05..012945782 100644 --- a/packages/candid/src/idl.ts +++ b/packages/candid/src/idl.ts @@ -914,10 +914,25 @@ export class OptClass extends ConstructType<[T] | []> { typeTable.add(this, concat(opCode, buffer)); } + public checkType(t: Type): Type { + try { + return super.checkType(t); + } catch (e) { + // try to coerce t to opt t + if (!(t instanceof OptClass) && !(this._type instanceof OptClass)) { + if (this._type.checkType(t)) { + return t; + } + } + // rethrow if opt coercion is not applicable + throw e; + } + } + public decodeValue(b: Pipe, t: Type): [T] | [] { const opt = this.checkType(t); if (!(opt instanceof OptClass)) { - throw new Error('Not an option type'); + return [this._type.decodeValue(b, opt)]; } switch (safeReadUint8(b)) { case 0: