You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(ts-interface-generator): support non-default-export classes (#476)
- create appropriate interface for classes which are not default
exports; this will make cases work when the default export is an
*instance* of the class (but it still requires the class itself to be
exported as named export, so the module augmentation can kick in).
- Add new way of writing finer-grained tests, so new cases can be
covered more easily
- Re-initialize base types for each generation to handle multiple
invocations in different type worlds properly - happens in tests
- Rename the "testdata" folder to "samples"
- add more testcases
- adapt README
Copy file name to clipboardExpand all lines: packages/ts-interface-generator/README.md
+22-25Lines changed: 22 additions & 25 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -55,6 +55,26 @@ This is the list of available command-line arguments, including the ones already
55
55
-`--loglevel`: Set the console logging verbosity; options are: `error`, `warn`, `info`, `debug`, `trace`; default level is `info`
56
56
-`--jsdoc`: Set the amount of JSDoc which should be generated; options are: `none`, `minimal`, `verbose`; default is `verbose`: by default, the JSDoc documentation written in the control metadata for the properties, aggregations etc. is added to the generated methods, plus generic documentation for the `@param` and `@returns` tags as well as some additional generic documentation like for default values (if any). By setting `--jsdoc none` or `--jsdoc minimal` you can decide to omit all JSDoc or to only add the JSDoc written in the control.
57
57
58
+
## Why?
59
+
60
+
When developing UI5 controls, the control metadata declares properties, aggregations, events etc. The methods related to these (like `getText()` for a `text` property or `attachPress(...)` for a `press` event) do not need to be implemented - they are generated by the UI5 framework at runtime.
61
+
62
+
However, as the TypeScript environment does not know about these methods, a call to any of them will be marked as error in the source code. And on top of this, there is also no code completion for these methods and no type information and in-place documentation for parameters and return values.
63
+
64
+
This is a problem for application code using controls developed in TypeScript as well as for the development of these controls in TypeScript. Both development scenarios involve calling those API methods which are not known to the TypeScript environment. Thus, there needs to be a way to make TypeScript aware of them.
65
+
66
+
(Remark: the controls shipped with the UI5 framework are implemented in JavaScript and a complete set of TypeScript type definitions is created during the framework build from the JSDoc comments. So both facets of the problem do not apply to them.)
67
+
68
+
## How it Works
69
+
70
+
This tool scans all TypeScript source files for <b>top-level definitions of classes</b> inheriting from `sap.ui.base.ManagedObject` (in most cases those might be sub-classes of `sap.ui.core.Control`, so they will be called "controls" for simplicity).
71
+
72
+
For any such control, the metadata is parsed, analyzed, and a new TypeScript file is constructed, which contains an interface declaration with the methods generated by UI5 at runtime. This generated interface gets merged with the already existing code using TypeScript's [Declaration Merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html) concept.
73
+
74
+
Unfortunately, these separate interface declarations cannot define new constructors (see e.g. [this related TS issue](https://github.com/microsoft/TypeScript/issues/2957)). Hence those must be manually added to each control (one-time effort, pasting 3 lines of code). The interface generator writes the required lines to the console.
75
+
76
+
Oh, and the tool itself is implemented in TypeScript because TypeScript makes development more efficient. ;-)
77
+
58
78
## Limitations
59
79
60
80
See the [TODO](#TODO) section for examples of features not yet implemented.
@@ -75,42 +95,19 @@ To detect whether the required constructor signatures are already present in the
75
95
76
96
### Second(+)-level Inheritance
77
97
78
-
When there are at least two levels of custom controls (inheriting from each other), there is the error message: `Class static side 'typeof SomeOtherControl' incorrectly extends base class static side 'typeof MyControl'. The types of 'metadata.properties' are incompatible between these types.`
79
-
80
-
As a workaround, you can assign a type to the `metadata` field in the parent class. Using type `object` is sufficient, but it can also be more specific:
98
+
When there are at least two levels of custom controls (inheriting from each other) and you get the error message: `Class static side 'typeof SomeOtherControl' incorrectly extends base class static side 'typeof MyControl'. The types of 'metadata.properties' are incompatible between these types.`, then you have missed to assign the `MetadataOptions` type to the `metadata` field in the parent class. (Before UI5 version 1.110, you can use the type `object` instead):
81
99
82
100
```ts
83
-
staticreadonlymetadata: object= {
101
+
staticreadonlymetadata: MetadataOptions= {
84
102
...
85
103
```
86
104
87
105
See [#338](https://github.com/SAP/ui5-typescript/issues/338) for more details.
88
106
89
-
## Why?
90
-
91
-
When developing UI5 controls, the control metadata declares properties, aggregations, events etc. The methods related to these (like `getText()` for a `text` property or `attachPress(...)` for a `press` event) do not need to be implemented - they are generated by the UI5 framework at runtime.
92
-
93
-
However, as the TypeScript environment does not know about these methods, a call to any of them will be marked as error in the source code. And on top of this, there is also no code completion for these methods and no type information and in-place documentation for parameters and return values.
94
-
95
-
This is a problem for application code using controls developed in TypeScript as well as for the development of these controls in TypeScript. Both development scenarios involve calling those API methods which are not known to the TypeScript environment. Thus, there needs to be a way to make TypeScript aware of them.
96
-
97
-
(Remark: the controls shipped with the UI5 framework are implemented in JavaScript and a complete set of TypeScript type definitions is created during the framework build from the JSDoc comments. So both facets of the problem do not apply to them.)
98
-
99
-
## How it Works
100
-
101
-
This tool scans all TypeScript source files for <b>top-level definitions of classes</b> inheriting from `sap.ui.base.ManagedObject` (in most cases those might be sub-classes of `sap.ui.core.Control`, so they will be called "controls" for simplicity).
102
-
103
-
For any such control, the metadata is parsed, analyzed, and a new TypeScript file is constructed, which contains an interface declaration with the methods generated by UI5 at runtime.
104
-
105
-
Unfortunately, these separate interface declarations cannot define new constructors (see e.g. [this related TS issue](https://github.com/microsoft/TypeScript/issues/2957)). Hence those must be manually added to each control (one-time effort, pasting 3 lines of code). The interface generator writes the required lines to the console.
106
-
107
-
Oh, and the tool itself is implemented in TypeScript because TypeScript makes development more efficient. ;-)
108
-
109
107
## TODO
110
108
111
109
- make sure watch mode does it right (also run on deletion? Delete interfaces before-creating? Only create interfaces for updated files?)
112
110
- consider further information like deprecation etc.
113
-
- it is probably required to check whether the control class being handled is the default export or a named export. Right now it is assumed that it is the default export. Other cases are not tested and likely not working.
// cache - TODO: needs to be refreshed when the UI5 type definitions are updated during a run of the tool!
39
+
// cache (execution takes one-digit milliseconds) - TODO: does it need to be refreshed when the UI5 type definitions are updated during a run of the tool, or is the clearing from generateInterfaces sufficient?
31
40
// identify the symbols for the interesting classes
32
41
constmanagedObjectModuleDeclaration=typeChecker
33
42
.getAmbientModules()
@@ -132,6 +141,7 @@ function generateInterfaces(
132
141
interfaceText: string,
133
142
)=>void=writeInterfaceFile,
134
143
){
144
+
resetBaseClasses();// typeChecker might be from a new type world
// @ts-ignore: below TS 4.8 there were more params
965
+
methods,
966
+
);
967
+
}else{
968
+
myInterface=factory.createInterfaceDeclaration(
969
+
undefined,
970
+
[],// no export needed for module augmentation when class is a named export in the original file!
971
+
classInfo.name,
972
+
undefined,
973
+
undefined,
974
+
// @ts-ignore: below TS 4.8 there were more params
975
+
methods,
976
+
);
977
+
}
908
978
}
909
979
addLineBreakBefore(myInterface,2);
910
980
@@ -945,8 +1015,9 @@ function buildAST(
945
1015
statements.push(genericEventDefinitionModule);
946
1016
}
947
1017
948
-
// if needed, assemble the second module declaration
949
-
if(requiredImports.selfIsUsed){
1018
+
// If needed, assemble the second module declaration.
1019
+
// In case the class is not a default export, the first module declaration will already be without export, so this second module declaration is not needed anyway
1020
+
if(requiredImports.selfIsUsed&&isDefaultExport){
950
1021
letmyInterface2;
951
1022
if(parseFloat(ts.version)>=4.8){
952
1023
myInterface2=factory.createInterfaceDeclaration(
@@ -988,7 +1059,7 @@ function buildAST(
988
1059
ts.addSyntheticLeadingComment(
989
1060
module2,
990
1061
ts.SyntaxKind.SingleLineCommentTrivia,
991
-
" this duplicate interface without export is needed to avoid \"Cannot find name '"+
1062
+
" this duplicate interface without export is needed to avoid \"Cannot find name '"+// TODO: does not seem to be needed any longer; investigate and try to reproduce
0 commit comments