TODO: figure out how to determine locations of used:
a) interfaces
b) type aliases
c) union types
d) complex types like Array<MyType>
There should be two scenarios:
a) Type is imported from a library
b) Type is imported from an adjacent module
c) Type is privately declared in current module
This uses TypeScript's TypeChecker to determine the types and the code
is a little cleaner, but still needs a lot of work:
- Determine how to figure out class type parameters
The goal is to generate reusable interfaces from entity definitions.
While class definitions could be made to implement interfaces, this
seems like too much work at this point.
As seen in TeamService2.ts, the Contextual type will add Context as the
last argument to any method of the interface, as long as the method has
0-4 arguments.
Interfaces with more than 4 arguments cannot use this type, but they
could be converted to interfaces which use 1 argument
(object/dictionary) only.
Some long-term thinking: Maybe only methods with a single argument
should be supported, similar to the way gRPC does it.
In this case the interface does not define context, but the implementing
service may use it as an optional argument:
interface IService {
add(a: number, b: number): number
}
class Service implements IService {
add(a: number, b: number, ctx?: IContext): number {
return a + b + ctx!.userId
}
}
A few notes:
1) The context higher-order function will be tedious to define - a lot
more typing than just an extra function argument.
2) As some methods do not require the context function, forgetting to
call it might introduce bugs (await fn will not error out), but
compile checks of the return value type might detect this
Possible solution is to go the GRPC way and allow only a single method
type as a parameter so all server-side method types would look like:
async fn(param: IParam, context: Context) {}
and all client-side method types would look like:
async fn(param: IParam) {}
However, This would deviate from the JSON-RPC standard which allows
multiple arguments to be passed in an array.
Alternatively, context could be passed as a first argument and then
filtered out:
type Arguments<T> = T extends (context: Ctx, ...args: infer A) => infer R
? A
: never
In this case, the type of Ctx would need to be known in advance. Will
have to think about this some more.
Typeorm loads all @Entities and decorator-defined relations into a
global variable, and thus makes it impossible to override already
defined variables.
Will have to think about how to disable this behaviour in case the user
of this library does not want to use predefined variables, but for now
we will use the predefined defaults.