diff --git a/packages/react-native-codegen/e2e/__tests__/modules/__snapshots__/GenerateModuleObjCpp-test.js.snap b/packages/react-native-codegen/e2e/__tests__/modules/__snapshots__/GenerateModuleObjCpp-test.js.snap index 9506703cc55..a59e8e0a1d0 100644 --- a/packages/react-native-codegen/e2e/__tests__/modules/__snapshots__/GenerateModuleObjCpp-test.js.snap +++ b/packages/react-native-codegen/e2e/__tests__/modules/__snapshots__/GenerateModuleObjCpp-test.js.snap @@ -434,7 +434,7 @@ namespace JS { struct Builder { struct Input { std::optional D; - id _Nullable A; + id _Nullable A; std::optional E; NSString *F; }; @@ -1780,7 +1780,7 @@ namespace JS { struct Builder { struct Input { std::optional D; - id _Nullable A; + id _Nullable A; std::optional E; NSString *F; }; diff --git a/packages/react-native-codegen/src/generators/TypeUtils/Cxx/index.js b/packages/react-native-codegen/src/generators/TypeUtils/Cxx/index.js new file mode 100644 index 00000000000..fcde7828a85 --- /dev/null +++ b/packages/react-native-codegen/src/generators/TypeUtils/Cxx/index.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + * @oncall react_native + */ + +function wrapOptional(type: string, isRequired: boolean): string { + return isRequired ? type : `std::optional<${type}>`; +} + +module.exports = { + wrapOptional, +}; diff --git a/packages/react-native-codegen/src/generators/TypeUtils/Java/index.js b/packages/react-native-codegen/src/generators/TypeUtils/Java/index.js new file mode 100644 index 00000000000..6253ea37d4d --- /dev/null +++ b/packages/react-native-codegen/src/generators/TypeUtils/Java/index.js @@ -0,0 +1,27 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + * @oncall react_native + */ + +const objectTypeForPrimitiveType = { + boolean: 'Boolean', + double: 'Double', + float: 'Float', + int: 'Integer', +}; + +function wrapOptional(type: string, isRequired: boolean): string { + return isRequired + ? type + : `@Nullable ${objectTypeForPrimitiveType[type] ?? type}`; +} + +module.exports = { + wrapOptional, +}; diff --git a/packages/react-native-codegen/src/generators/TypeUtils/Objective-C/index.js b/packages/react-native-codegen/src/generators/TypeUtils/Objective-C/index.js new file mode 100644 index 00000000000..529bfe64a9a --- /dev/null +++ b/packages/react-native-codegen/src/generators/TypeUtils/Objective-C/index.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + * @oncall react_native + */ + +function wrapOptional(type: string, isRequired: boolean): string { + return isRequired ? type : `${type} _Nullable`; +} + +module.exports = { + wrapOptional, +}; diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js index b6272f2d065..6cfbdd06097 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js @@ -27,6 +27,7 @@ import type { import type {AliasResolver} from './Utils'; const {unwrapNullable} = require('../../parsers/parsers-commons'); +const {wrapOptional} = require('../TypeUtils/Cxx'); const {getEnumName, toSafeCppString} = require('../Utils'); const {indent} = require('../Utils'); const { @@ -139,26 +140,21 @@ function translatePrimitiveJSTypeToCpp( const [typeAnnotation, nullable] = unwrapNullable( nullableTypeAnnotation, ); - const isRequired = !optional && !nullable; const isRecursiveType = isDirectRecursiveMember( parentObjectAliasName, nullableTypeAnnotation, ); - + const isRequired = (!optional && !nullable) || isRecursiveType; let realTypeAnnotation = typeAnnotation; if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { realTypeAnnotation = resolveAlias(realTypeAnnotation.name); } - function wrap(type: string) { - return isRequired || isRecursiveType ? type : `std::optional<${type}>`; - } - switch (realTypeAnnotation.type) { case 'ReservedTypeAnnotation': switch (realTypeAnnotation.name) { case 'RootTag': - return wrap('double'); + return wrapOptional('double', isRequired); default: (realTypeAnnotation.name: empty); throw new Error(createErrorMessage(realTypeAnnotation.name)); @@ -166,49 +162,49 @@ function translatePrimitiveJSTypeToCpp( case 'VoidTypeAnnotation': return 'void'; case 'StringTypeAnnotation': - return wrap('jsi::String'); + return wrapOptional('jsi::String', isRequired); case 'NumberTypeAnnotation': - return wrap('double'); + return wrapOptional('double', isRequired); case 'DoubleTypeAnnotation': - return wrap('double'); + return wrapOptional('double', isRequired); case 'FloatTypeAnnotation': - return wrap('double'); + return wrapOptional('double', isRequired); case 'Int32TypeAnnotation': - return wrap('int'); + return wrapOptional('int', isRequired); case 'BooleanTypeAnnotation': - return wrap('bool'); + return wrapOptional('bool', isRequired); case 'EnumDeclaration': switch (realTypeAnnotation.memberType) { case 'NumberTypeAnnotation': - return wrap('jsi::Value'); + return wrapOptional('jsi::Value', isRequired); case 'StringTypeAnnotation': - return wrap('jsi::String'); + return wrapOptional('jsi::String', isRequired); default: throw new Error(createErrorMessage(realTypeAnnotation.type)); } case 'GenericObjectTypeAnnotation': - return wrap('jsi::Object'); + return wrapOptional('jsi::Object', isRequired); case 'UnionTypeAnnotation': switch (typeAnnotation.memberType) { case 'NumberTypeAnnotation': - return wrap('double'); + return wrapOptional('double', isRequired); case 'ObjectTypeAnnotation': - return wrap('jsi::Object'); + return wrapOptional('jsi::Object', isRequired); case 'StringTypeAnnotation': - return wrap('jsi::String'); + return wrapOptional('jsi::String', isRequired); default: throw new Error(createErrorMessage(realTypeAnnotation.type)); } case 'ObjectTypeAnnotation': - return wrap('jsi::Object'); + return wrapOptional('jsi::Object', isRequired); case 'ArrayTypeAnnotation': - return wrap('jsi::Array'); + return wrapOptional('jsi::Array', isRequired); case 'FunctionTypeAnnotation': - return wrap('jsi::Function'); + return wrapOptional('jsi::Function', isRequired); case 'PromiseTypeAnnotation': - return wrap('jsi::Value'); + return wrapOptional('jsi::Value', isRequired); case 'MixedTypeAnnotation': - return wrap('jsi::Value'); + return wrapOptional('jsi::Value', isRequired); default: (realTypeAnnotation.type: empty); throw new Error(createErrorMessage(realTypeAnnotation.type)); diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js index 3eba2a53eb5..219978bb3a5 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js @@ -22,6 +22,7 @@ import type { import type {AliasResolver} from './Utils'; const {unwrapNullable} = require('../../parsers/parsers-commons'); +const {wrapOptional} = require('../TypeUtils/Java'); const {createAliasResolver, getModules} = require('./Utils'); type FilesOutput = Map; @@ -111,13 +112,8 @@ function translateFunctionParamToJavaType( const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation); const isRequired = !optional && !nullable; - - function wrapNullable(javaType: string, nullableType?: string) { - if (!isRequired) { - imports.add('javax.annotation.Nullable'); - return `@Nullable ${nullableType ?? javaType}`; - } - return javaType; + if (!isRequired) { + imports.add('javax.annotation.Nullable'); } // FIXME: support class alias for args @@ -130,41 +126,41 @@ function translateFunctionParamToJavaType( case 'ReservedTypeAnnotation': switch (realTypeAnnotation.name) { case 'RootTag': - return wrapNullable('double', 'Double'); + return wrapOptional('double', isRequired); default: (realTypeAnnotation.name: empty); throw new Error(createErrorMessage(realTypeAnnotation.name)); } case 'StringTypeAnnotation': - return wrapNullable('String'); + return wrapOptional('String', isRequired); case 'NumberTypeAnnotation': - return wrapNullable('double', 'Double'); + return wrapOptional('double', isRequired); case 'FloatTypeAnnotation': - return wrapNullable('double', 'Double'); + return wrapOptional('double', isRequired); case 'DoubleTypeAnnotation': - return wrapNullable('double', 'Double'); + return wrapOptional('double', isRequired); case 'Int32TypeAnnotation': - return wrapNullable('double', 'Double'); + return wrapOptional('double', isRequired); case 'BooleanTypeAnnotation': - return wrapNullable('boolean', 'Boolean'); + return wrapOptional('boolean', isRequired); case 'EnumDeclaration': switch (realTypeAnnotation.memberType) { case 'NumberTypeAnnotation': - return wrapNullable('double', 'Double'); + return wrapOptional('double', isRequired); case 'StringTypeAnnotation': - return wrapNullable('String'); + return wrapOptional('String', isRequired); default: throw new Error(createErrorMessage(realTypeAnnotation.type)); } case 'UnionTypeAnnotation': switch (typeAnnotation.memberType) { case 'NumberTypeAnnotation': - return wrapNullable('double', 'Double'); + return wrapOptional('double', isRequired); case 'ObjectTypeAnnotation': imports.add('com.facebook.react.bridge.ReadableMap'); - return wrapNullable('ReadableMap'); + return wrapOptional('ReadableMap', isRequired); case 'StringTypeAnnotation': - return wrapNullable('String'); + return wrapOptional('String', isRequired); default: throw new Error( `Unsupported union member returning value, found: ${realTypeAnnotation.memberType}"`, @@ -172,17 +168,17 @@ function translateFunctionParamToJavaType( } case 'ObjectTypeAnnotation': imports.add('com.facebook.react.bridge.ReadableMap'); - return wrapNullable('ReadableMap'); + return wrapOptional('ReadableMap', isRequired); case 'GenericObjectTypeAnnotation': // Treat this the same as ObjectTypeAnnotation for now. imports.add('com.facebook.react.bridge.ReadableMap'); - return wrapNullable('ReadableMap'); + return wrapOptional('ReadableMap', isRequired); case 'ArrayTypeAnnotation': imports.add('com.facebook.react.bridge.ReadableArray'); - return wrapNullable('ReadableArray'); + return wrapOptional('ReadableArray', isRequired); case 'FunctionTypeAnnotation': imports.add('com.facebook.react.bridge.Callback'); - return wrapNullable('Callback'); + return wrapOptional('Callback', isRequired); default: (realTypeAnnotation.type: 'MixedTypeAnnotation'); throw new Error(createErrorMessage(realTypeAnnotation.type)); @@ -200,14 +196,12 @@ function translateFunctionReturnTypeToJavaType( nullableReturnTypeAnnotation, ); - function wrapNullable(javaType: string, nullableType?: string) { - if (nullable) { - imports.add('javax.annotation.Nullable'); - return `@Nullable ${nullableType ?? javaType}`; - } - return javaType; + if (nullable) { + imports.add('javax.annotation.Nullable'); } + const isRequired = !nullable; + // FIXME: support class alias for args let realTypeAnnotation = returnTypeAnnotation; if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') { @@ -218,7 +212,7 @@ function translateFunctionReturnTypeToJavaType( case 'ReservedTypeAnnotation': switch (realTypeAnnotation.name) { case 'RootTag': - return wrapNullable('double', 'Double'); + return wrapOptional('double', isRequired); default: (realTypeAnnotation.name: empty); throw new Error(createErrorMessage(realTypeAnnotation.name)); @@ -228,35 +222,35 @@ function translateFunctionReturnTypeToJavaType( case 'PromiseTypeAnnotation': return 'void'; case 'StringTypeAnnotation': - return wrapNullable('String'); + return wrapOptional('String', isRequired); case 'NumberTypeAnnotation': - return wrapNullable('double', 'Double'); + return wrapOptional('double', isRequired); case 'FloatTypeAnnotation': - return wrapNullable('double', 'Double'); + return wrapOptional('double', isRequired); case 'DoubleTypeAnnotation': - return wrapNullable('double', 'Double'); + return wrapOptional('double', isRequired); case 'Int32TypeAnnotation': - return wrapNullable('double', 'Double'); + return wrapOptional('double', isRequired); case 'BooleanTypeAnnotation': - return wrapNullable('boolean', 'Boolean'); + return wrapOptional('boolean', isRequired); case 'EnumDeclaration': switch (realTypeAnnotation.memberType) { case 'NumberTypeAnnotation': - return wrapNullable('double', 'Double'); + return wrapOptional('double', isRequired); case 'StringTypeAnnotation': - return wrapNullable('String'); + return wrapOptional('String', isRequired); default: throw new Error(createErrorMessage(realTypeAnnotation.type)); } case 'UnionTypeAnnotation': switch (realTypeAnnotation.memberType) { case 'NumberTypeAnnotation': - return wrapNullable('double', 'Double'); + return wrapOptional('double', isRequired); case 'ObjectTypeAnnotation': imports.add('com.facebook.react.bridge.WritableMap'); - return wrapNullable('WritableMap'); + return wrapOptional('WritableMap', isRequired); case 'StringTypeAnnotation': - return wrapNullable('String'); + return wrapOptional('String', isRequired); default: throw new Error( `Unsupported union member returning value, found: ${realTypeAnnotation.memberType}"`, @@ -264,13 +258,13 @@ function translateFunctionReturnTypeToJavaType( } case 'ObjectTypeAnnotation': imports.add('com.facebook.react.bridge.WritableMap'); - return wrapNullable('WritableMap'); + return wrapOptional('WritableMap', isRequired); case 'GenericObjectTypeAnnotation': imports.add('com.facebook.react.bridge.WritableMap'); - return wrapNullable('WritableMap'); + return wrapOptional('WritableMap', isRequired); case 'ArrayTypeAnnotation': imports.add('com.facebook.react.bridge.WritableArray'); - return wrapNullable('WritableArray'); + return wrapOptional('WritableArray', isRequired); default: (realTypeAnnotation.type: 'MixedTypeAnnotation'); throw new Error(createErrorMessage(realTypeAnnotation.type)); diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js index c9e8c2d6a0d..ad7918d686f 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeConstantsStruct.js @@ -15,6 +15,10 @@ import type {ConstantsStruct, StructTypeAnnotation} from '../StructCollector'; import type {StructSerilizationOutput} from './serializeStruct'; const {unwrapNullable} = require('../../../../parsers/parsers-commons'); +const {wrapOptional: wrapCxxOptional} = require('../../../TypeUtils/Cxx'); +const { + wrapOptional: wrapObjCOptional, +} = require('../../../TypeUtils/Objective-C'); const {capitalize} = require('../../../Utils'); const {getNamespacedStructName, getSafePropertyName} = require('../Utils'); @@ -78,15 +82,12 @@ function toObjCType( ): string { const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation); const isRequired = !nullable && !isOptional; - const wrapOptional = (type: string) => { - return isRequired ? type : `std::optional<${type}>`; - }; switch (typeAnnotation.type) { case 'ReservedTypeAnnotation': switch (typeAnnotation.name) { case 'RootTag': - return wrapOptional('double'); + return wrapCxxOptional('double', isRequired); default: (typeAnnotation.name: empty); throw new Error(`Unknown prop type, found: ${typeAnnotation.name}"`); @@ -94,19 +95,19 @@ function toObjCType( case 'StringTypeAnnotation': return 'NSString *'; case 'NumberTypeAnnotation': - return wrapOptional('double'); + return wrapCxxOptional('double', isRequired); case 'FloatTypeAnnotation': - return wrapOptional('double'); + return wrapCxxOptional('double', isRequired); case 'Int32TypeAnnotation': - return wrapOptional('double'); + return wrapCxxOptional('double', isRequired); case 'DoubleTypeAnnotation': - return wrapOptional('double'); + return wrapCxxOptional('double', isRequired); case 'BooleanTypeAnnotation': - return wrapOptional('bool'); + return wrapCxxOptional('bool', isRequired); case 'EnumDeclaration': switch (typeAnnotation.memberType) { case 'NumberTypeAnnotation': - return wrapOptional('double'); + return wrapCxxOptional('double', isRequired); case 'StringTypeAnnotation': return 'NSString *'; default: @@ -115,17 +116,18 @@ function toObjCType( ); } case 'GenericObjectTypeAnnotation': - return isRequired ? 'id ' : 'id _Nullable '; + return wrapObjCOptional('id', isRequired); case 'ArrayTypeAnnotation': if (typeAnnotation.elementType == null) { - return isRequired ? 'id ' : 'id _Nullable '; + return wrapObjCOptional('id', isRequired); } - return wrapOptional( + return wrapCxxOptional( `std::vector<${toObjCType( hasteModuleName, typeAnnotation.elementType, )}>`, + isRequired, ); case 'TypeAliasTypeAnnotation': const structName = capitalize(typeAnnotation.name); @@ -133,7 +135,7 @@ function toObjCType( hasteModuleName, structName, ); - return wrapOptional(`${namespacedStructName}::Builder`); + return wrapCxxOptional(`${namespacedStructName}::Builder`, isRequired); default: (typeAnnotation.type: empty); throw new Error( diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js index ec2a9d2f576..9ea93a1da7a 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/header/serializeRegularStruct.js @@ -15,6 +15,10 @@ import type {RegularStruct, StructTypeAnnotation} from '../StructCollector'; import type {StructSerilizationOutput} from './serializeStruct'; const {unwrapNullable} = require('../../../../parsers/parsers-commons'); +const {wrapOptional: wrapCxxOptional} = require('../../../TypeUtils/Cxx'); +const { + wrapOptional: wrapObjCOptional, +} = require('../../../TypeUtils/Objective-C'); const {capitalize} = require('../../../Utils'); const {getNamespacedStructName, getSafePropertyName} = require('../Utils'); @@ -69,15 +73,12 @@ function toObjCType( ): string { const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation); const isRequired = !nullable && !isOptional; - const wrapOptional = (type: string) => { - return isRequired ? type : `std::optional<${type}>`; - }; switch (typeAnnotation.type) { case 'ReservedTypeAnnotation': switch (typeAnnotation.name) { case 'RootTag': - return wrapOptional('double'); + return wrapCxxOptional('double', isRequired); default: (typeAnnotation.name: empty); throw new Error(`Unknown prop type, found: ${typeAnnotation.name}"`); @@ -85,19 +86,19 @@ function toObjCType( case 'StringTypeAnnotation': return 'NSString *'; case 'NumberTypeAnnotation': - return wrapOptional('double'); + return wrapCxxOptional('double', isRequired); case 'FloatTypeAnnotation': - return wrapOptional('double'); + return wrapCxxOptional('double', isRequired); case 'Int32TypeAnnotation': - return wrapOptional('double'); + return wrapCxxOptional('double', isRequired); case 'DoubleTypeAnnotation': - return wrapOptional('double'); + return wrapCxxOptional('double', isRequired); case 'BooleanTypeAnnotation': - return wrapOptional('bool'); + return wrapCxxOptional('bool', isRequired); case 'EnumDeclaration': switch (typeAnnotation.memberType) { case 'NumberTypeAnnotation': - return wrapOptional('double'); + return wrapCxxOptional('double', isRequired); case 'StringTypeAnnotation': return 'NSString *'; default: @@ -106,16 +107,17 @@ function toObjCType( ); } case 'GenericObjectTypeAnnotation': - return isRequired ? 'id ' : 'id _Nullable'; + return wrapObjCOptional('id', isRequired); case 'ArrayTypeAnnotation': if (typeAnnotation.elementType == null) { - return isRequired ? 'id ' : 'id _Nullable'; + return wrapObjCOptional('id', isRequired); } - return wrapOptional( + return wrapCxxOptional( `facebook::react::LazyVector<${toObjCType( hasteModuleName, typeAnnotation.elementType, )}>`, + isRequired, ); case 'TypeAliasTypeAnnotation': const structName = capitalize(typeAnnotation.name); @@ -123,7 +125,7 @@ function toObjCType( hasteModuleName, structName, ); - return wrapOptional(namespacedStructName); + return wrapCxxOptional(namespacedStructName, isRequired); default: (typeAnnotation.type: empty); throw new Error( diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js index 40feab503d4..0bf872f0dae 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js @@ -24,6 +24,7 @@ const { unwrapNullable, wrapNullable, } = require('../../../parsers/parsers-commons'); +const {wrapOptional} = require('../../TypeUtils/Objective-C'); const {capitalize} = require('../../Utils'); const {getNamespacedStructName} = require('./Utils'); const invariant = require('invariant'); @@ -188,11 +189,7 @@ function getParamObjCType( ): $ReadOnly<{objCType: string, isStruct: boolean}> { const {name: paramName, typeAnnotation: nullableTypeAnnotation} = param; const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation); - const notRequired = param.optional || nullable; - - function wrapIntoNullableIfNeeded(generatedType: string) { - return nullable ? `${generatedType} _Nullable` : generatedType; - } + const isRequired = !param.optional && !nullable; const isStruct = (objCType: string) => ({ isStruct: true, @@ -220,7 +217,7 @@ function getParamObjCType( * type Animal = {}; * Array => NSArray, etc. */ - return notStruct(wrapIntoNullableIfNeeded('NSArray *')); + return notStruct(wrapOptional('NSArray *', !nullable)); } } @@ -251,7 +248,7 @@ function getParamObjCType( case 'ReservedTypeAnnotation': switch (structTypeAnnotation.name) { case 'RootTag': - return notStruct(notRequired ? 'NSNumber *' : 'double'); + return notStruct(isRequired ? 'double' : 'NSNumber *'); default: (structTypeAnnotation.name: empty); throw new Error( @@ -259,30 +256,30 @@ function getParamObjCType( ); } case 'StringTypeAnnotation': - return notStruct(wrapIntoNullableIfNeeded('NSString *')); + return notStruct(wrapOptional('NSString *', !nullable)); case 'NumberTypeAnnotation': - return notStruct(notRequired ? 'NSNumber *' : 'double'); + return notStruct(isRequired ? 'double' : 'NSNumber *'); case 'FloatTypeAnnotation': - return notStruct(notRequired ? 'NSNumber *' : 'double'); + return notStruct(isRequired ? 'double' : 'NSNumber *'); case 'DoubleTypeAnnotation': - return notStruct(notRequired ? 'NSNumber *' : 'double'); + return notStruct(isRequired ? 'double' : 'NSNumber *'); case 'Int32TypeAnnotation': - return notStruct(notRequired ? 'NSNumber *' : 'double'); + return notStruct(isRequired ? 'double' : 'NSNumber *'); case 'BooleanTypeAnnotation': - return notStruct(notRequired ? 'NSNumber *' : 'BOOL'); + return notStruct(isRequired ? 'BOOL' : 'NSNumber *'); case 'EnumDeclaration': switch (typeAnnotation.memberType) { case 'NumberTypeAnnotation': - return notStruct(notRequired ? 'NSNumber *' : 'double'); + return notStruct(isRequired ? 'double' : 'NSNumber *'); case 'StringTypeAnnotation': - return notStruct(wrapIntoNullableIfNeeded('NSString *')); + return notStruct(wrapOptional('NSString *', !nullable)); default: throw new Error( `Unsupported enum type for param "${paramName}" in ${methodName}. Found: ${typeAnnotation.type}`, ); } case 'GenericObjectTypeAnnotation': - return notStruct(wrapIntoNullableIfNeeded('NSDictionary *')); + return notStruct(wrapOptional('NSDictionary *', !nullable)); default: (structTypeAnnotation.type: empty); throw new Error( @@ -296,10 +293,7 @@ function getReturnObjCType( nullableTypeAnnotation: Nullable, ): string { const [typeAnnotation, nullable] = unwrapNullable(nullableTypeAnnotation); - - function wrapIntoNullableIfNeeded(generatedType: string) { - return nullable ? `${generatedType} _Nullable` : generatedType; - } + const isRequired = !nullable; switch (typeAnnotation.type) { case 'VoidTypeAnnotation': @@ -307,24 +301,25 @@ function getReturnObjCType( case 'PromiseTypeAnnotation': return 'void'; case 'ObjectTypeAnnotation': - return wrapIntoNullableIfNeeded('NSDictionary *'); + return wrapOptional('NSDictionary *', isRequired); case 'TypeAliasTypeAnnotation': - return wrapIntoNullableIfNeeded('NSDictionary *'); + return wrapOptional('NSDictionary *', isRequired); case 'ArrayTypeAnnotation': if (typeAnnotation.elementType == null) { - return wrapIntoNullableIfNeeded('NSArray> *'); + return wrapOptional('NSArray> *', isRequired); } - return wrapIntoNullableIfNeeded( + return wrapOptional( `NSArray<${getReturnObjCType( methodName, typeAnnotation.elementType, )}> *`, + isRequired, ); case 'ReservedTypeAnnotation': switch (typeAnnotation.name) { case 'RootTag': - return wrapIntoNullableIfNeeded('NSNumber *'); + return wrapOptional('NSNumber *', isRequired); default: (typeAnnotation.name: empty); throw new Error( @@ -334,23 +329,23 @@ function getReturnObjCType( case 'StringTypeAnnotation': // TODO: Can NSString * returns not be _Nullable? // In the legacy codegen, we don't surround NSSTring * with _Nullable - return wrapIntoNullableIfNeeded('NSString *'); + return wrapOptional('NSString *', isRequired); case 'NumberTypeAnnotation': - return wrapIntoNullableIfNeeded('NSNumber *'); + return wrapOptional('NSNumber *', isRequired); case 'FloatTypeAnnotation': - return wrapIntoNullableIfNeeded('NSNumber *'); + return wrapOptional('NSNumber *', isRequired); case 'DoubleTypeAnnotation': - return wrapIntoNullableIfNeeded('NSNumber *'); + return wrapOptional('NSNumber *', isRequired); case 'Int32TypeAnnotation': - return wrapIntoNullableIfNeeded('NSNumber *'); + return wrapOptional('NSNumber *', isRequired); case 'BooleanTypeAnnotation': - return wrapIntoNullableIfNeeded('NSNumber *'); + return wrapOptional('NSNumber *', isRequired); case 'EnumDeclaration': switch (typeAnnotation.memberType) { case 'NumberTypeAnnotation': - return wrapIntoNullableIfNeeded('NSNumber *'); + return wrapOptional('NSNumber *', isRequired); case 'StringTypeAnnotation': - return wrapIntoNullableIfNeeded('NSString *'); + return wrapOptional('NSString *', isRequired); default: throw new Error( `Unsupported enum return type for ${methodName}. Found: ${typeAnnotation.type}`, @@ -359,20 +354,20 @@ function getReturnObjCType( case 'UnionTypeAnnotation': switch (typeAnnotation.memberType) { case 'NumberTypeAnnotation': - return wrapIntoNullableIfNeeded('NSNumber *'); + return wrapOptional('NSNumber *', isRequired); case 'ObjectTypeAnnotation': - return wrapIntoNullableIfNeeded('NSDictionary *'); + return wrapOptional('NSDictionary *', isRequired); case 'StringTypeAnnotation': // TODO: Can NSString * returns not be _Nullable? // In the legacy codegen, we don't surround NSSTring * with _Nullable - return wrapIntoNullableIfNeeded('NSString *'); + return wrapOptional('NSString *', isRequired); default: throw new Error( `Unsupported union return type for ${methodName}, found: ${typeAnnotation.memberType}"`, ); } case 'GenericObjectTypeAnnotation': - return wrapIntoNullableIfNeeded('NSDictionary *'); + return wrapOptional('NSDictionary *', isRequired); default: (typeAnnotation.type: 'MixedTypeAnnotation'); throw new Error(