diff --git a/crates/napi/src/js_values/mod.rs b/crates/napi/src/js_values/mod.rs index f4e0d8e4..2deeffa8 100644 --- a/crates/napi/src/js_values/mod.rs +++ b/crates/napi/src/js_values/mod.rs @@ -347,7 +347,7 @@ macro_rules! impl_object_methods { pub fn get_named_property(&self, name: &str) -> Result where - T: FromNapiValue, + T: FromNapiValue + ValidateNapiValue, { let key = CString::new(name)?; let mut raw_value = ptr::null_mut(); @@ -357,6 +357,12 @@ macro_rules! impl_object_methods { }, "get_named_property error" )?; + unsafe { ::validate(self.0.env, raw_value) }.map_err( + |mut err| { + err.reason = format!("Object property '{name}' type mismatch. {}", err.reason); + err + }, + )?; unsafe { ::from_napi_value(self.0.env, raw_value) } } diff --git a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md index 3d5302db..f529533c 100644 --- a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md +++ b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md @@ -515,6 +515,8 @@ Generated by [AVA](https://avajs.dev). bird: Bird␊ }␊ ␊ + export function objectGetNamedPropertyShouldPerformTypecheck(obj: { foo: number; bar: string; }): void␊ + ␊ export interface ObjectOnlyFromJs {␊ count: number␊ callback: (err: Error | null, arg: number) => any␊ diff --git a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap index ac1ef45a..1d5b5c85 100644 Binary files a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap and b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap differ diff --git a/examples/napi/__tests__/__snapshots__/values.spec.ts.snap b/examples/napi/__tests__/__snapshots__/values.spec.ts.snap index 22206d79..d4241d4d 100644 Binary files a/examples/napi/__tests__/__snapshots__/values.spec.ts.snap and b/examples/napi/__tests__/__snapshots__/values.spec.ts.snap differ diff --git a/examples/napi/__tests__/values.spec.ts b/examples/napi/__tests__/values.spec.ts index 1296ea45..a0be79d8 100644 --- a/examples/napi/__tests__/values.spec.ts +++ b/examples/napi/__tests__/values.spec.ts @@ -105,6 +105,7 @@ import { createExternalTypedArray, mutateTypedArray, receiveAllOptionalObject, + objectGetNamedPropertyShouldPerformTypecheck, fnReceivedAliased, ALIAS, appendBuffer, @@ -487,6 +488,36 @@ Napi4Test('callback function return Promise and spawn', async (t) => { test('object', (t) => { t.deepEqual(listObjKeys({ name: 'John Doe', age: 20 }), ['name', 'age']) t.deepEqual(createObj(), { test: 1 }) + t.throws( + () => + objectGetNamedPropertyShouldPerformTypecheck({ + // @ts-expect-error + foo: '2', + bar: '3', + }), + { + message: `Object property 'foo' type mismatch. Expect value to be Number, but received String`, + code: 'InvalidArg', + }, + ) + t.throws( + () => + objectGetNamedPropertyShouldPerformTypecheck({ + foo: 2, + // @ts-expect-error + bar: 3, + }), + { + message: `Object property 'bar' type mismatch. Expect value to be String, but received Number`, + code: 'InvalidArg', + }, + ) + t.notThrows(() => + objectGetNamedPropertyShouldPerformTypecheck({ + foo: 2, + bar: '3', + }), + ) }) test('get str from object', (t) => { diff --git a/examples/napi/index.cjs b/examples/napi/index.cjs index a90e361e..15c078f9 100644 --- a/examples/napi/index.cjs +++ b/examples/napi/index.cjs @@ -476,6 +476,7 @@ module.exports.listObjKeys = nativeBinding.listObjKeys module.exports.mapOption = nativeBinding.mapOption module.exports.mutateExternal = nativeBinding.mutateExternal module.exports.mutateTypedArray = nativeBinding.mutateTypedArray +module.exports.objectGetNamedPropertyShouldPerformTypecheck = nativeBinding.objectGetNamedPropertyShouldPerformTypecheck module.exports.optionEnd = nativeBinding.optionEnd module.exports.optionOnly = nativeBinding.optionOnly module.exports.optionStart = nativeBinding.optionStart diff --git a/examples/napi/index.d.cts b/examples/napi/index.d.cts index 215487f3..40f22290 100644 --- a/examples/napi/index.d.cts +++ b/examples/napi/index.d.cts @@ -505,6 +505,8 @@ export interface ObjectFieldClassInstance { bird: Bird } +export function objectGetNamedPropertyShouldPerformTypecheck(obj: { foo: number; bar: string; }): void + export interface ObjectOnlyFromJs { count: number callback: (err: Error | null, arg: number) => any diff --git a/examples/napi/src/object.rs b/examples/napi/src/object.rs index 63ee457f..480b0f60 100644 --- a/examples/napi/src/object.rs +++ b/examples/napi/src/object.rs @@ -124,3 +124,10 @@ fn receive_object_only_from_js( ); }); } + +#[napi(ts_args_type = "obj: { foo: number; bar: string; }")] +fn object_get_named_property_should_perform_typecheck(obj: Object) -> Result<()> { + obj.get_named_property::("foo")?; + obj.get_named_property::("bar")?; + Ok(()) +}