From 33d1291e1a96497a4f994e9d622248a745ee1ea6 Mon Sep 17 00:00:00 2001 From: "Zihan Chen (MSFT)" <53799235+ZihanChen-MSFT@users.noreply.github.com> Date: Thu, 21 Jul 2022 06:38:59 -0700 Subject: [PATCH] Support TypeScript array types for turbo module (component only) (#34216) Summary: Turbo module codegen for component parse `readonly T[]` and `readonly Object[][]` incorrectly. In this change, it is fixed, with necessary test cases added. ## Changelog [General] [Added] - Support TypeScript array types for turbo module (component only) Pull Request resolved: https://github.com/facebook/react-native/pull/34216 Test Plan: `yarn jest` passed in `packages/react-native-codegen` Reviewed By: sammy-SC Differential Revision: D37953004 Pulled By: cipolleschi fbshipit-source-id: b71ea35eb9c4bd5dc41130f1dcf9201704ec298e --- .../components/__test_fixtures__/fixtures.js | 133 ++++ .../typescript-component-parser-test.js.snap | 684 ++++++++++++++++++ .../parsers/typescript/components/props.js | 84 ++- 3 files changed, 872 insertions(+), 29 deletions(-) diff --git a/packages/react-native-codegen/src/parsers/typescript/components/__test_fixtures__/fixtures.js b/packages/react-native-codegen/src/parsers/typescript/components/__test_fixtures__/fixtures.js index cb1357c40127a1..e27afe6a4b6ea1 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/__test_fixtures__/fixtures.js @@ -459,6 +459,138 @@ export default codegenNativeComponent( ) as HostComponent; `; +const ARRAY2_PROP_TYPES_NO_EVENTS = ` +/** + * 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. + * + * @format + */ + +'use strict'; + +const codegenNativeComponent = require('codegenNativeComponent'); + +import type {Int32, Double, Float, WithDefault} from 'CodegenTypes'; +import type {ImageSource} from 'ImageSource'; +import type { + ColorValue, + ColorArrayValue, + PointValue, + EdgeInsetsValue, +} from 'StyleSheetTypes'; +import type {ViewProps} from 'ViewPropTypes'; +import type {HostComponent} from 'react-native'; + +type ObjectType = Readonly<{prop: string}>; +type ArrayObjectType = readonly Readonly<{prop: string}>[]; + +export interface ModuleProps extends ViewProps { + // Props + // Boolean props + array_boolean_required: readonly boolean[]; + array_boolean_optional_key?: readonly boolean[]; + array_boolean_optional_value: readonly boolean[] | null | undefined; + array_boolean_optional_both?: readonly boolean[] | null | undefined; + + // String props + array_string_required: readonly string[]; + array_string_optional_key?: readonly string[]; + array_string_optional_value: readonly string[] | null | undefined; + array_string_optional_both?: readonly string[] | null | undefined; + + // Double props + array_double_required: readonly Double[]; + array_double_optional_key?: readonly Double[]; + array_double_optional_value: readonly Double[] | null | undefined; + array_double_optional_both?: readonly Double[] | null | undefined; + + // Float props + array_float_required: readonly Float[]; + array_float_optional_key?: readonly Float[]; + array_float_optional_value: readonly Float[] | null | undefined; + array_float_optional_both?: readonly Float[] | null | undefined; + + // Int32 props + array_int32_required: readonly Int32[]; + array_int32_optional_key?: readonly Int32[]; + array_int32_optional_value: readonly Int32[] | null | undefined; + array_int32_optional_both?: readonly Int32[] | null | undefined; + + // String enum props + array_enum_optional_key?: WithDefault< + readonly ('small' | 'large')[], + 'small' + >; + array_enum_optional_both?: WithDefault< + readonly ('small' | 'large')[], + 'small' + >; + + // ImageSource props + array_image_required: readonly ImageSource[]; + array_image_optional_key?: readonly ImageSource[]; + array_image_optional_value: readonly ImageSource[] | null | undefined; + array_image_optional_both?: readonly ImageSource[] | null | undefined; + + // ColorValue props + array_color_required: readonly ColorValue[]; + array_color_optional_key?: readonly ColorValue[]; + array_color_optional_value: readonly ColorValue[] | null | undefined; + array_color_optional_both?: readonly ColorValue[] | null | undefined; + + // PointValue props + array_point_required: readonly PointValue[]; + array_point_optional_key?: readonly PointValue[]; + array_point_optional_value: readonly PointValue[] | null | undefined; + array_point_optional_both?: readonly PointValue[] | null | undefined; + + // EdgeInsetsValue props + array_insets_required: readonly EdgeInsetsValue[]; + array_insets_optional_key?: readonly EdgeInsetsValue[]; + array_insets_optional_value: readonly EdgeInsetsValue[] | null | undefined; + array_insets_optional_both?: readonly EdgeInsetsValue[] | null | undefined; + + // Object props + array_object_required: readonly Readonly<{prop: string}>[]; + array_object_optional_key?: readonly Readonly<{prop: string}>[]; + array_object_optional_value: ArrayObjectType | null | undefined; + array_object_optional_both?: readonly ObjectType[] | null | undefined; + + // Nested array object types + array_of_array_object_required: readonly Readonly<{ + // This needs to be the same name as the top level array above + array_object_required: readonly Readonly<{prop: string}>[]; + }>[]; + array_of_array_object_optional_key?: readonly Readonly<{ + // This needs to be the same name as the top level array above + array_object_optional_key: readonly Readonly<{prop?: string}>[]; + }>[]; + array_of_array_object_optional_value: readonly Readonly<{ + // This needs to be the same name as the top level array above + array_object_optional_value: readonly Readonly<{prop: string | null | undefined}>[]; + }>[] | null | undefined; + array_of_array_object_optional_both?: readonly Readonly<{ + // This needs to be the same name as the top level array above + array_object_optional_both: readonly Readonly<{prop?: string | null | undefined}>[]; + }>[] | null | undefined; + + // Nested array of array of object types + array_of_array_of_object_required: readonly Readonly<{ + prop: string; + }>[][]; + + // Nested array of array of object types (in file) + array_of_array_of_object_required_in_file: readonly ObjectType[][]; +} + +export default codegenNativeComponent( + 'Module', +) as HostComponent; +`; + const OBJECT_PROP_TYPES_NO_EVENTS = ` /** * Copyright (c) Meta Platforms, Inc. and affiliates. @@ -959,6 +1091,7 @@ export default codegenNativeComponent( module.exports = { ALL_PROP_TYPES_NO_EVENTS, ARRAY_PROP_TYPES_NO_EVENTS, + ARRAY2_PROP_TYPES_NO_EVENTS, OBJECT_PROP_TYPES_NO_EVENTS, PROPS_ALIASED_LOCALLY, ONE_OF_EACH_PROP_EVENT_DEFAULT_AND_OPTIONS, diff --git a/packages/react-native-codegen/src/parsers/typescript/components/__tests__/__snapshots__/typescript-component-parser-test.js.snap b/packages/react-native-codegen/src/parsers/typescript/components/__tests__/__snapshots__/typescript-component-parser-test.js.snap index bfaa95fdbf9840..27ea1405c86e2f 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/__tests__/__snapshots__/typescript-component-parser-test.js.snap +++ b/packages/react-native-codegen/src/parsers/typescript/components/__tests__/__snapshots__/typescript-component-parser-test.js.snap @@ -1231,6 +1231,690 @@ exports[`RN Codegen TypeScript Parser can generate fixture ARRAY_PROP_TYPES_NO_E }" `; +exports[`RN Codegen TypeScript Parser can generate fixture ARRAY2_PROP_TYPES_NO_EVENTS 1`] = ` +"{ + 'modules': { + 'Module': { + 'type': 'Component', + 'components': { + 'Module': { + 'extendsProps': [ + { + 'type': 'ReactNativeBuiltInType', + 'knownTypeName': 'ReactNativeCoreViewProps' + } + ], + 'events': [], + 'props': [ + { + 'name': 'array_boolean_required', + 'optional': false, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'BooleanTypeAnnotation' + } + } + }, + { + 'name': 'array_boolean_optional_key', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'BooleanTypeAnnotation' + } + } + }, + { + 'name': 'array_boolean_optional_value', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'BooleanTypeAnnotation' + } + } + }, + { + 'name': 'array_boolean_optional_both', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'BooleanTypeAnnotation' + } + } + }, + { + 'name': 'array_string_required', + 'optional': false, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'StringTypeAnnotation' + } + } + }, + { + 'name': 'array_string_optional_key', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'StringTypeAnnotation' + } + } + }, + { + 'name': 'array_string_optional_value', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'StringTypeAnnotation' + } + } + }, + { + 'name': 'array_string_optional_both', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'StringTypeAnnotation' + } + } + }, + { + 'name': 'array_double_required', + 'optional': false, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'DoubleTypeAnnotation' + } + } + }, + { + 'name': 'array_double_optional_key', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'DoubleTypeAnnotation' + } + } + }, + { + 'name': 'array_double_optional_value', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'DoubleTypeAnnotation' + } + } + }, + { + 'name': 'array_double_optional_both', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'DoubleTypeAnnotation' + } + } + }, + { + 'name': 'array_float_required', + 'optional': false, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'FloatTypeAnnotation' + } + } + }, + { + 'name': 'array_float_optional_key', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'FloatTypeAnnotation' + } + } + }, + { + 'name': 'array_float_optional_value', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'FloatTypeAnnotation' + } + } + }, + { + 'name': 'array_float_optional_both', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'FloatTypeAnnotation' + } + } + }, + { + 'name': 'array_int32_required', + 'optional': false, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'Int32TypeAnnotation' + } + } + }, + { + 'name': 'array_int32_optional_key', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'Int32TypeAnnotation' + } + } + }, + { + 'name': 'array_int32_optional_value', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'Int32TypeAnnotation' + } + } + }, + { + 'name': 'array_int32_optional_both', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'Int32TypeAnnotation' + } + } + }, + { + 'name': 'array_enum_optional_key', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'StringEnumTypeAnnotation', + 'default': 'small', + 'options': [ + 'small', + 'large' + ] + } + } + }, + { + 'name': 'array_enum_optional_both', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'StringEnumTypeAnnotation', + 'default': 'small', + 'options': [ + 'small', + 'large' + ] + } + } + }, + { + 'name': 'array_image_required', + 'optional': false, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ReservedPropTypeAnnotation', + 'name': 'ImageSourcePrimitive' + } + } + }, + { + 'name': 'array_image_optional_key', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ReservedPropTypeAnnotation', + 'name': 'ImageSourcePrimitive' + } + } + }, + { + 'name': 'array_image_optional_value', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ReservedPropTypeAnnotation', + 'name': 'ImageSourcePrimitive' + } + } + }, + { + 'name': 'array_image_optional_both', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ReservedPropTypeAnnotation', + 'name': 'ImageSourcePrimitive' + } + } + }, + { + 'name': 'array_color_required', + 'optional': false, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ReservedPropTypeAnnotation', + 'name': 'ColorPrimitive' + } + } + }, + { + 'name': 'array_color_optional_key', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ReservedPropTypeAnnotation', + 'name': 'ColorPrimitive' + } + } + }, + { + 'name': 'array_color_optional_value', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ReservedPropTypeAnnotation', + 'name': 'ColorPrimitive' + } + } + }, + { + 'name': 'array_color_optional_both', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ReservedPropTypeAnnotation', + 'name': 'ColorPrimitive' + } + } + }, + { + 'name': 'array_point_required', + 'optional': false, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ReservedPropTypeAnnotation', + 'name': 'PointPrimitive' + } + } + }, + { + 'name': 'array_point_optional_key', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ReservedPropTypeAnnotation', + 'name': 'PointPrimitive' + } + } + }, + { + 'name': 'array_point_optional_value', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ReservedPropTypeAnnotation', + 'name': 'PointPrimitive' + } + } + }, + { + 'name': 'array_point_optional_both', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ReservedPropTypeAnnotation', + 'name': 'PointPrimitive' + } + } + }, + { + 'name': 'array_insets_required', + 'optional': false, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ReservedPropTypeAnnotation', + 'name': 'EdgeInsetsPrimitive' + } + } + }, + { + 'name': 'array_insets_optional_key', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ReservedPropTypeAnnotation', + 'name': 'EdgeInsetsPrimitive' + } + } + }, + { + 'name': 'array_insets_optional_value', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ReservedPropTypeAnnotation', + 'name': 'EdgeInsetsPrimitive' + } + } + }, + { + 'name': 'array_insets_optional_both', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ReservedPropTypeAnnotation', + 'name': 'EdgeInsetsPrimitive' + } + } + }, + { + 'name': 'array_object_required', + 'optional': false, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ObjectTypeAnnotation', + 'properties': [ + { + 'name': 'prop', + 'optional': false, + 'typeAnnotation': { + 'type': 'StringTypeAnnotation', + 'default': null + } + } + ] + } + } + }, + { + 'name': 'array_object_optional_key', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ObjectTypeAnnotation', + 'properties': [ + { + 'name': 'prop', + 'optional': false, + 'typeAnnotation': { + 'type': 'StringTypeAnnotation', + 'default': null + } + } + ] + } + } + }, + { + 'name': 'array_object_optional_value', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ObjectTypeAnnotation', + 'properties': [ + { + 'name': 'prop', + 'optional': false, + 'typeAnnotation': { + 'type': 'StringTypeAnnotation', + 'default': null + } + } + ] + } + } + }, + { + 'name': 'array_object_optional_both', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ObjectTypeAnnotation', + 'properties': [ + { + 'name': 'prop', + 'optional': false, + 'typeAnnotation': { + 'type': 'StringTypeAnnotation', + 'default': null + } + } + ] + } + } + }, + { + 'name': 'array_of_array_object_required', + 'optional': false, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ObjectTypeAnnotation', + 'properties': [ + { + 'name': 'array_object_required', + 'optional': false, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ObjectTypeAnnotation', + 'properties': [ + { + 'name': 'prop', + 'optional': false, + 'typeAnnotation': { + 'type': 'StringTypeAnnotation', + 'default': null + } + } + ] + } + } + } + ] + } + } + }, + { + 'name': 'array_of_array_object_optional_key', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ObjectTypeAnnotation', + 'properties': [ + { + 'name': 'array_object_optional_key', + 'optional': false, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ObjectTypeAnnotation', + 'properties': [ + { + 'name': 'prop', + 'optional': true, + 'typeAnnotation': { + 'type': 'StringTypeAnnotation', + 'default': null + } + } + ] + } + } + } + ] + } + } + }, + { + 'name': 'array_of_array_object_optional_value', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ObjectTypeAnnotation', + 'properties': [ + { + 'name': 'array_object_optional_value', + 'optional': false, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ObjectTypeAnnotation', + 'properties': [ + { + 'name': 'prop', + 'optional': true, + 'typeAnnotation': { + 'type': 'StringTypeAnnotation', + 'default': null + } + } + ] + } + } + } + ] + } + } + }, + { + 'name': 'array_of_array_object_optional_both', + 'optional': true, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ObjectTypeAnnotation', + 'properties': [ + { + 'name': 'array_object_optional_both', + 'optional': false, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ObjectTypeAnnotation', + 'properties': [ + { + 'name': 'prop', + 'optional': true, + 'typeAnnotation': { + 'type': 'StringTypeAnnotation', + 'default': null + } + } + ] + } + } + } + ] + } + } + }, + { + 'name': 'array_of_array_of_object_required', + 'optional': false, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ObjectTypeAnnotation', + 'properties': [ + { + 'name': 'prop', + 'optional': false, + 'typeAnnotation': { + 'type': 'StringTypeAnnotation', + 'default': null + } + } + ] + } + } + } + }, + { + 'name': 'array_of_array_of_object_required_in_file', + 'optional': false, + 'typeAnnotation': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'ObjectTypeAnnotation', + 'properties': [ + { + 'name': 'prop', + 'optional': false, + 'typeAnnotation': { + 'type': 'StringTypeAnnotation', + 'default': null + } + } + ] + } + } + } + } + ], + 'commands': [] + } + } + } + } +}" +`; + exports[`RN Codegen TypeScript Parser can generate fixture COMMANDS_AND_EVENTS_TYPES_EXPORTED 1`] = ` "{ 'modules': { diff --git a/packages/react-native-codegen/src/parsers/typescript/components/props.js b/packages/react-native-codegen/src/parsers/typescript/components/props.js index 74659ff0dc6ec9..ff2f94c00e5a78 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/props.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/props.js @@ -41,12 +41,54 @@ function getPropProperties( } } +function getTypeAnnotationForObjectAsArrayElement( + objectType: $FlowFixMe, + types: TypeDeclarationMap, +) { + return { + type: 'ObjectTypeAnnotation', + properties: flattenProperties( + objectType.typeParameters.params[0].members || + objectType.typeParameters.params, + types, + ) + .map(prop => buildPropSchema(prop, types)) + .filter(Boolean), + }; +} + +function getTypeAnnotationForArrayOfArrayOfObject( + typeAnnotation: $FlowFixMe, + types: TypeDeclarationMap, +) { + // We need to go yet another level deeper to resolve + // types that may be defined in a type alias + const nestedObjectType = getValueFromTypes(typeAnnotation, types); + + return { + type: 'ArrayTypeAnnotation', + elementType: getTypeAnnotationForObjectAsArrayElement( + nestedObjectType, + types, + ), + }; +} + function getTypeAnnotationForArray( name: string, typeAnnotation: $FlowFixMe, defaultValue: $FlowFixMe | null, types: TypeDeclarationMap, ) { + if (typeAnnotation.type === 'TSParenthesizedType') { + return getTypeAnnotationForArray( + name, + typeAnnotation.typeAnnotation, + defaultValue, + types, + ); + } + const extractedTypeAnnotation = getValueFromTypes(typeAnnotation, types); if ( @@ -56,7 +98,7 @@ function getTypeAnnotationForArray( ) ) { throw new Error( - 'Nested optionals such as "ReadonlyArray" are not supported, please declare optionals at the top level of value definitions as in "ReadonlyArray | null | undefined"', + 'Nested optionals such as "ReadonlyArray" are not supported, please declare optionals at the top level of value definitions as in "ReadonlyArray | null | undefined"', ); } @@ -69,44 +111,28 @@ function getTypeAnnotationForArray( ); } + // Covers: T[] + if (typeAnnotation.type === 'TSArrayType') { + return getTypeAnnotationForArrayOfArrayOfObject( + typeAnnotation.elementType, + types, + ); + } + if (extractedTypeAnnotation.type === 'TSTypeReference') { // Resolve the type alias if it's not defined inline const objectType = getValueFromTypes(extractedTypeAnnotation, types); if (objectType.typeName.name === 'Readonly') { - return { - type: 'ObjectTypeAnnotation', - properties: flattenProperties( - objectType.typeParameters.params[0].members || - objectType.typeParameters.params, - types, - ) - .map(prop => buildPropSchema(prop, types)) - .filter(Boolean), - }; + return getTypeAnnotationForObjectAsArrayElement(objectType, types); } + // Covers: ReadonlyArray if (objectType.typeName.name === 'ReadonlyArray') { - // We need to go yet another level deeper to resolve - // types that may be defined in a type alias - const nestedObjectType = getValueFromTypes( + return getTypeAnnotationForArrayOfArrayOfObject( objectType.typeParameters.params[0], types, ); - - return { - type: 'ArrayTypeAnnotation', - elementType: { - type: 'ObjectTypeAnnotation', - properties: flattenProperties( - nestedObjectType.typeParameters.params[0].members || - nestedObjectType.typeParameters.params, - types, - ) - .map(prop => buildPropSchema(prop, types)) - .filter(Boolean), - }, - }; } } @@ -239,7 +265,7 @@ function getTypeAnnotation( type: 'ArrayTypeAnnotation', elementType: getTypeAnnotationForArray( name, - typeAnnotation.typeAnnotation, + typeAnnotation.typeAnnotation.elementType, defaultValue, types, ),