Repository: DevExpress/devextreme-react
Pull request #560 (2021-05-20 08:45:45)

Add type for complex options
Fix T996069


Patch:
diff --git a/packages/devextreme-react-generator/src/component-generator.ts b/packages/devextreme-react-generator/src/component-generator.ts
index 5f3c85d..d9d8b1c 100644
--- a/packages/devextreme-react-generator/src/component-generator.ts
+++ b/packages/devextreme-react-generator/src/component-generator.ts
@@ -48,6 +48,7 @@ type IOption = {
   name: string;
   isSubscribable?: true;
   isArray?: boolean;
+  type?: any
 } & ({
   type: string;
   nested?: undefined;
@@ -115,7 +116,8 @@ function renderObject(props: IOption[], indent: number): string {
   props.forEach((opt) => {
     result += `\n${getIndent(indent)}${opt.name}?: `;
     if (opt.nested && isNotEmptyArray(opt.nested)) {
-      result += renderObject(opt.nested, indent);
+      const type = opt.type ? `${opt.type} | ` : '';
+      result += `${type}${renderObject(opt.nested, indent)}`;
       if (opt.isArray) { result += '[]'; }
     } else {
       result += opt.type;
diff --git a/packages/devextreme-react-generator/src/generator.ts b/packages/devextreme-react-generator/src/generator.ts
index 4cf565f..25dc390 100644
--- a/packages/devextreme-react-generator/src/generator.ts
+++ b/packages/devextreme-react-generator/src/generator.ts
@@ -39,6 +39,14 @@ import {
   uppercaseFirst,
 } from './helpers';
 
+enum BaseTypes {
+  Any = 'any',
+  String = 'string',
+  Number = 'number',
+  Boolean = 'boolean',
+  Object = 'object'
+}
+
 export function mapSubscribableOption(prop: IProp): ISubscribableOption {
   return {
     name: prop.name,
@@ -57,6 +65,16 @@ export function isNestedOptionArray(prop: IProp): boolean {
   return isNotEmptyArray(prop.types) && (prop.types[0].type === 'Array');
 }
 
+export function convertToBaseType(type: string) {
+  return BaseTypes[type];
+}
+
+export function getComplexOptionType(types: ITypeDescr[]) {
+  return types && isNotEmptyArray(types) ? types
+    .filter((t) => convertToBaseType(t.type))
+    .map((f) => convertToBaseType(f.type)).join(' | ') : undefined;
+}
+
 export function mapOption(prop: IProp): IOption {
   return isEmptyArray(prop.props)
     ? {
@@ -67,6 +85,7 @@ export function mapOption(prop: IProp): IOption {
     } : {
       name: prop.name,
       isSubscribable: prop.isSubscribable || undefined,
+      type: getComplexOptionType(prop.types),
       nested: (prop.props as IProp[]).map(mapOption),
       isArray: isNestedOptionArray(prop),
     };


Test patch:
diff --git a/packages/devextreme-react-generator/src/component-generator.test.ts b/packages/devextreme-react-generator/src/component-generator.test.ts
index e9e2143..65e64b7 100644
--- a/packages/devextreme-react-generator/src/component-generator.test.ts
+++ b/packages/devextreme-react-generator/src/component-generator.test.ts
@@ -414,9 +414,9 @@ class CLASS_NAME extends BaseComponent<ICLASS_NAMEOptions> {
 // CLASS_NAME
 interface IOpt_1_ComponentProps {
   sub_opt_2?: TYPE_1;
-  sub_opt_3?: {
+  sub_opt_3?: TYPE_SUB_3 | {
     sub_sub_opt_4?: TYPE_2;
-    sub_sub_opt_5?: {
+    sub_sub_opt_5?: TYPE_SUB_OPT_5 | TYPE_SUB_OPT_6 | {
       sub_sub_sub_opt_6?: TYPE_3;
     };
   };
@@ -465,6 +465,7 @@ export {
               },
               {
                 name: 'sub_opt_3',
+                type: 'TYPE_SUB_3',
                 nested: [
                   {
                     name: 'sub_sub_opt_4',
@@ -472,6 +473,7 @@ export {
                   },
                   {
                     name: 'sub_sub_opt_5',
+                    type: 'TYPE_SUB_OPT_5 | TYPE_SUB_OPT_6',
                     nested: [
                       {
                         name: 'sub_sub_sub_opt_6',
diff --git a/packages/devextreme-react-generator/src/generator.test.ts b/packages/devextreme-react-generator/src/generator.test.ts
index cd4dd2e..efdc6f7 100644
--- a/packages/devextreme-react-generator/src/generator.test.ts
+++ b/packages/devextreme-react-generator/src/generator.test.ts
@@ -2,8 +2,10 @@ import { IProp, IComplexProp } from './integration-data-model';
 import {
   collectIndependentEvents,
   collectSubscribableRecursively,
+  getComplexOptionType,
   mapSubscribableOption,
   isNestedOptionArray,
+  convertToBaseType,
   mapOption,
   extractNestedComponents,
   createPropTyping,
@@ -297,6 +299,50 @@ describe('mapOption', () => {
     });
   });
 
+  it('complex option with types', () => {
+    const option = {
+      name: 'option',
+      isSubscribable: false,
+      types: [{
+        type: 'String',
+        acceptableValues: [],
+        isCustomType: false,
+      }, {
+        type: 'Number',
+        acceptableValues: [],
+        isCustomType: false,
+      }, {
+        type: 'MyType',
+        acceptableValues: [],
+        isCustomType: false,
+      }],
+      props: [{
+        name: 'prop1',
+        firedEvents: [],
+        isSubscribable: false,
+        props: [],
+        types: [{
+          type: 'Number',
+          acceptableValues: [],
+          isCustomType: false,
+        }],
+      }],
+      firedEvents: [],
+    };
+
+    expect(mapOption(option)).toEqual({
+      isArray: false,
+      name: 'option',
+      type: 'string | number',
+      nested: [{
+        isSubscribable: undefined,
+        name: 'prop1',
+        type: 'any',
+      }],
+      isSubscribable: undefined,
+    });
+  });
+
   it('with props', () => {
     const option = {
       name: 'option',
@@ -656,4 +702,39 @@ describe('mapWidget', () => {
       }]);
     });
   });
+  describe('convertToBaseType', () => {
+    const types = ['Object', 'MyType', 'Number', 'String', 'Boolean', 'Any'];
+    const expected = ['object', undefined, 'number', 'string', 'boolean', 'any'];
+
+    it('should return base types', () => {
+      expect(types.map((t) => convertToBaseType(t))).toEqual(expected);
+    });
+  });
+
+  describe('getComplexOptionType', () => {
+    const types = [{
+      type: 'String',
+      acceptableValues: [],
+      isCustomType: false,
+    }, {
+      type: 'Number',
+      acceptableValues: [],
+      isCustomType: false,
+    }, {
+      type: 'Object',
+      acceptableValues: [],
+      isCustomType: false,
+    }, {
+      type: 'MyType',
+      acceptableValues: [],
+      isCustomType: true,
+    },
+    ];
+
+    const expected = 'string | number | object';
+
+    it('should return base types', () => {
+      expect(getComplexOptionType(types)).toEqual(expected);
+    });
+  });
 });