I'm trying to create a generic "mapping" method that can take an interface and JSON response then map the available keys to the interface. The issue I'm having is there seems no way to get the available properties of an interface without initializing them.
Is there a way to get the value of an interface without having to define it?
Interfaces and other type information (with exceptions) are not available at runtime.
However, you can define a method that deserializes a JSON string into a given type. const myThing: IThing = JSON.parse(myJsonString); will cast it to the type IThing.
If you have a class Thing that implements IThing, you can use Object.keys to iterate over the keys of an instantiated Thing, and match the key indices together, like thing[key] = JSON.parse(...)[key]
interface and types in typescript are only checked when compiling (from ts to js). So, at runtime (which runs js), there is no information about interface.
But you can implement this with use of class and reflect-metadata and decorators, like class-transformer do.
Simply switching my interfaces to a class was the solution. I am then able to create a blank instance and list out all available properties.
Related
I have some code that is needs to hydrate objects with types that are not known at compile time. (The code is in a library) From the type name, I would like to discover the type and construct it dynamically at runtime. The important operation here is running the default constructor and doing the other setup expected from the prototype. Is that even possible in typescript? How would I do it?
As #pan93412 said, Typescript's types are largely erased and not reified at runtime, and this is an explicit project goal. I would suggest trying to create a map of known type names to class constructors (or factory methods), and using those to reconstitute the objects:
https://www.typescriptlang.org/play?#code/MYGwhgzhAEDqCWATA5gUwC7QN4Chr+gHck10B9AM3lREWgF5oByMJnAXx1EhgBUALeADtk2PAXSCRlarQbMARm05cA9kIiZeATQAKAUQDK83ATgkMALnMoMAGnH4Bw5NeciO0SNmgBtdACeAA6o1poATi4AutZgQgHQKsDqmtAQqJFgIPAAXqh0jKYEqgoAVqjA6BDWvkwItuhMdszuyExRHDhqGqogqAB0IKrIABTpmdl5iP0l5ZUQ-QC2YEEjqgwAfNBCqITQOgaGvqpRIwCUF0A
Did you ask something about something like interface or type alias? Well, it is not possible since TypeScript will clean out all the type information in the compiled JS - The type information would only be used on the type-checking of tsc, the metadata (need to be explicitly enabled) and the generated .d.ts which can't be used in runtime.
It is better to give us more explicit examples so we would be able to provide a better answer. :-)
I'm not sure about how the metadata in TypeScript works. I didn't write such a code before.
I’m working on a web application users add objects to a database. The structure of these objects is fixed, but some of them have a data property that contains a map of strings, for example { title: "Test", description: "Description" }. Both the keys and the values in this map are user-defined, so theoretically users can define keys like constructor, __proto__ and __ob__.
I want to store the database objects in the data of a Vue 2 component, so they will be made reactive. I'm having trouble to decide which data type I should use for the data property. The following options come to my mind:
Usually I would use a null-prototype object for the data property. So create it using Object.create(null). This would make sure that keys like constructor and __proto__ would work without problems, but there would be problems with the __ob__ property added by Vue. I’m not sure if deleting that property would cause any trouble for Vue, but I guess it will just add the property again on the next occasion.
The proper way to do it would be to use ES6 maps. But as far as I understand, Vue 2 doesn’t support these yet, so they won’t be reactive.
I could use or write a custom class that has an interface similar to ES6 maps but internally stores the data in an array or something else that Vue’s reactivity system supports. There seem to be plenty of ES6 map polyfills out there, but I’m not sure which ones of them would work flawlessly with Vue’s reactivity system.
What would be the best way to store such a map of values with user-defined keys in a reactive Vue 2 object?
In the end I wrote my own class that has an interface similar to ES6 maps, so that migrating to ES6 maps will be straightforward when I migrate to Vue 3 one day.
In my class, I'm storing the map entries in an array of [string, string] tuples. I'm using an array because Vue 2 has good reactivity support for arrays, and I'm using tuples because then the array's iterator has the same data type as an ES6 map iterator. You can find the source code of my class here.
Other solutions to the problem were suggested in the comments:
Use a prefix for the property keys
Use the Vue 3 composition API in Vue 2 using a plugin
is it possible to dynamically change the values of an enum? I have an enum like this
enum MyEnum{
Error = "Error",
Warn = "Warnings"
}
I then realized that the values here Error and Warnings have to dynamically generated by react-i18next during the runtime for them to be translated into different languages. So I wonder if there is a way to dynamically plug in the values for the enum. The keys would remain the same all the time.
Well enum in TS are constant and you cannot modify the object once you have declared it. Also, if you are working with internationalisation, using enums are not the best approach, unless you want to translate them every time when a page load or a function gets called.
TS Enum reference: https://www.typescriptlang.org/docs/handbook/enums.html
So I have an external models/interfaces package published via npm, it is built using typegoose + mongoose, so I can have my interfaces in one place and then use the interfaces/models across multiple apps whilst also keeping them in sync.
Here's and example of a model/class.
Now the issue is, I'm making some dynamic tables on the frontend to work with either collection, so I'm trying to find a way to get the keys as strings whilst keeping it all dynamic in one place.
So far I've tried ts-transformer-keys but due to having to implement some stuff around compile-time, it won't work inside angular.
I also can't use the class directly into angular due to the class extending Typegoose which depends on mongoose which throws an error about some global thing and then crashes the whole app.
Where User is my class which extends Typegoose and IUser is the type from that class, things like:
class test extends User {}
still have mongo dependency error
class test implements User{}
still need to manually add all properties
Object.keys(User)
mongoose dependency will throw an error
Object.keys(IUser)
doesn't woork because type doesn't exist at runtime;
Perhaps a decorator on the class that loops over all the keys & adds the constructor name to the class prototype? That doesn't seem likely to be working at runtime.
For future readers, read this github issue, TL;DR: typegoose currently dosnt support clientside classes
How can I instantiate a class if all I know is its name, given the following restrictions?
ES6
The class is defined by a third-party. I have no way of knowing about the class ahead of time.
All of the answers I've seen on Stackoverflow assume that I define the class being instantiated, and as such I can create a mapping between class names and their constructions ahead of time. Example: https://stackoverflow.com/a/31790015/14731
Seeing as this cannot be done for 3rd-party classes, what can I do?
Is eval() the only way?
What I am trying to do
Users are expected to pass in a class name and I am supposed to instantiate the class, assuming the existence of a constructor that takes exactly one String argument. More specifically, I am allowing users to override the type of exception my library will throw on error.
What worked for me:
Instead of having users pass in the name of the exception they wanted to instantiate, I just had them pass in the exception constructor.