diff --git a/metadata-generator/src/TSEmitter/TypeSpec.cpp b/metadata-generator/src/TSEmitter/TypeSpec.cpp index ad65bbb62..3dcdee653 100644 --- a/metadata-generator/src/TSEmitter/TypeSpec.cpp +++ b/metadata-generator/src/TSEmitter/TypeSpec.cpp @@ -66,7 +66,8 @@ std::string TSFile::typeToString(const TypeSpec& type, bool isStatic, bool isRet result += ") => "; result += typeToString(*type.callbackReturn, false, true); if (type.isNullable) { - result += " | null"; + // `() => T | null` would make the return type nullable instead. + result = "(" + result + ") | null"; } break; } diff --git a/packages/objc-node-api/index.d.ts b/packages/objc-node-api/index.d.ts index 021f11abb..f6b1e610f 100644 --- a/packages/objc-node-api/index.d.ts +++ b/packages/objc-node-api/index.d.ts @@ -9,6 +9,27 @@ declare global { class NativeObject implements interop.PointerObject { readonly __brand__: interop.PointerObject["__brand__"]; + + /** + * Registers an Objective-C subclass of this class. Functions in `methods` + * override the same-named members of this class and of + * `options.protocols`; other names are only callable from Objective-C + * when listed in `options.exposedMethods`. The returned class cannot + * itself be extended. + */ + static extend< + // deno-lint-ignore no-explicit-any + This extends abstract new (...args: any) => any, + // Inferred rather than annotated so that names absent from the class + // (protocol and exposed methods) are not excess properties. `& object` + // keeps literals sharing no name with the class from failing the weak + // type check against the all-optional Partial. + Methods extends Partial> & object, + >( + this: This, + methods: Methods & ThisType & Methods>, + options?: objc.ExtendOptions, + ): This; } namespace objc { @@ -25,6 +46,19 @@ declare global { ptr: interop.Pointer, size: number, ): ArrayBuffer; + + export interface ExposedMethod { + returns?: unknown; + params?: unknown[]; + } + + export interface ExtendOptions { + /** Objective-C class name; a numeric suffix is added if it is taken. */ + name?: string; + protocols?: unknown[]; + /** Keyed by method name in `methods`. */ + exposedMethods?: Record; + } } namespace interop { diff --git a/packages/react-native/cli/configure.js b/packages/react-native/cli/configure.js index e2e271e10..79c4a015d 100755 --- a/packages/react-native/cli/configure.js +++ b/packages/react-native/cli/configure.js @@ -26,8 +26,9 @@ function printNextSteps() { console.log(' iOS : cd ios && RCT_NEW_ARCH_ENABLED=1 USE_HERMES=1 pod install'); console.log(' Android : npx react-native run-android'); console.log(''); - console.log(' Android typings are generated on demand:'); - console.log(' cd android && ./gradlew generateNativeScriptTypingsDebug -PnsGenerateTypings=true'); + console.log(' Android typings are written to types/android/ on the first build'); + console.log(' (opt out with -PnsGenerateTypings=false). To generate them on their own:'); + console.log(' cd android && ./gradlew generateNativeScriptTypingsDebug'); } function configure(argv = process.argv.slice(2)) {