scala.js — getting complex objects from JavaScript - javascript

I'm trying scala.js and I must say that it's completely impressed! However, I try to introduce it into our production little-by-little, working side-by-side with existing JavaScript code. One thing I'm struggling with is passing complex structures from JS to Scala. For example, I have ready-made JS object that I've got from the other JS module:
h = {
"someInt": 123,
"someStr": "hello",
"someArray": [
{"name": "a book", "price": 123},
{"name": "a newspaper", "price": 456}
],
"someMap": {
"Knuth": {
"name": "The Art of Computer Programming",
"price": 789
},
"Gang of Four": {
"name": "Design Patterns: Blah-blah",
"price": 1234
}
}
}
It
has some ints, some strings (all these elements have fixed key names!), some arrays in it (which in turn has some more objects in it) and some maps (which map arbitrary string keys into more objects). Everything is optional and might be missing. Obviously, it's just a made-up example, real-life objects are much more complex, but all the basics are up here. I already have the corresponding class hierarchy in Scala which looks something like that:
case class MegaObject(
someInt: Option[Int],
someStr: Option[String],
someArray: Option[Seq[Item]],
someMap: Option[Map[String, Item]]
)
case class Item(name: Option[String], price: Option[Int])
1st attempt
My first try was to a naïve attempt to just use receiver types as is:
#JSExport
def try1(src: MegaObject): Unit = {
Console.println(src)
Console.println(src.someInt)
Console.println(src.someStr)
}
and it, obviously, fails with:
An undefined behavior was detected: [object Object] is not an instance of my.package.MainJs$MegaObject
2nd attempt
My second idea was receiving this object as js.Dictionary[String] and then doing lots of heavy typechecking & typecasting. First we'll define some helper methods to parse regular strings and integers from JS object:
def getOptStr(obj: js.Dictionary[String], key: String): Option[String] = {
if (obj.contains(key)) {
Some(obj(key))
} else {
None
}
}
def getOptInt(obj: js.Dictionary[String], key: String): Option[Int] = {
if (obj.contains(key)) {
Some(obj(key).asInstanceOf[Int])
} else {
None
}
}
Then we'll use them to parse an Item object from the same source:
def parseItem(src: js.Dictionary[String]): Item = {
val name = getOptStr(src, "name")
val price = getOptInt(src, "price")
Item(name, price)
}
And then, all together, to parse whole MegaObject:
#JSExport
def try2(src: js.Dictionary[String]): Unit = {
Console.println(src)
val someInt = getOptInt(src, "someInt")
val someStr = getOptStr(src, "someStr")
val someArray: Option[Seq[Item]] = if (src.contains("someArray")) {
Some(src("someArray").asInstanceOf[js.Array[js.Dictionary[String]]].map { item =>
parseItem(item)
})
} else {
None
}
val someMap: Option[Map[String, Item]] = if (src.contains("someMap")) {
val m = src("someMap").asInstanceOf[js.Dictionary[String]]
val r = m.keys.map { mapKey =>
val mapVal = m(mapKey).asInstanceOf[js.Dictionary[String]]
val item = parseItem(mapVal)
mapKey -> item
}.toMap
Some(r)
} else {
None
}
val result = MegaObject(someInt, someStr, someArray, someMap)
Console.println(result)
}
It, well, works, but it is really ugly. That's lots of code, lots of repetitions. It can probably refactored to extract array parsing and map parsing into something saner, but it still feels bad :(
3rd attempt
Tried the #ScalaJSDefined annotation to create something along the lines of "facade" class, as described in documentation:
#ScalaJSDefined
class JSMegaObject(
val someInt: js.Object,
val someStr: js.Object,
val someArray: js.Object,
val someMap: js.Object
) extends js.Object
Just printing it out kind of works:
#JSExport
def try3(src: JSMegaObject): Unit = {
Console.println(src)
Console.println(src.someInt)
Console.println(src.someStr)
Console.println(src.someArray)
Console.println(src.someMap)
}
However, as soon as I'm trying to add a method to JSMegaObject "facade" that will convert it to its proper Scala counterpart (even a fake one like this):
#ScalaJSDefined
class JSMegaObject(
val someInt: js.Object,
val someStr: js.Object,
val someArray: js.Object,
val someMap: js.Object
) extends js.Object {
def toScala: MegaObject = {
MegaObject(None, None, None, None)
}
}
trying to call it fails with:
An undefined behavior was detected: undefined is not an instance of my.package.MainJs$MegaObject
... which kind of really reminds me of attempt #1.
Obviously, one can still do all the typecasting in the main method:
#JSExport
def try3real(src: JSMegaObject): Unit = {
val someInt = if (src.someInt == js.undefined) {
None
} else {
Some(src.someInt.asInstanceOf[Int])
}
val someStr = if (src.someStr == js.undefined) {
None
} else {
Some(src.someStr.asInstanceOf[String])
}
// Think of some way to access maps and arrays here
val r = MegaObject(someInt, someStr, None, None)
Console.println(r)
}
However, it quickly becomes just as ugly as attempt #2.
Conclusion so far
So, I'm kind of frustrated. Attempts #2 and #3 do work, but it really feels that I'm missing something and it shouldn't be that ugly, uncomfortable, and require to write tons of JS-to-Scala types converter code just to access the fields of an incoming JS object. What is the better way to do it?

Your attempt #4 is close, but not quite there. What you want is not a Scala.js-defined JS class. You want an actual facade trait. Then you can "pimp" its conversion to your Scala class in its companion object. You must also be careful to always use js.UndefOr for optional fields.
#ScalaJSDefined
trait JSMegaObject extends js.Object {
val someInt: js.UndefOr[Int]
val someStr: js.UndefOr[String],
val someArray: js.UndefOr[js.Array[JSItem]],
val someMap: js.UndefOr[js.Dictionary[JSItem]]
}
object JSMegaObject {
implicit class JSMegaObjectOps(val self: JSMegaObject) extends AnyVal {
def toMegaObject: MegaObject = {
MegaObject(
self.someInt.toOption,
self.someStr.toOption,
self.someArray.toOption.map(_.map(_.toItem)),
self.someMap.toOption.map(_.mapValues(_.toItem)))
}
}
}
#ScalaJSDefined
trait JSItem extends js.Object {
val name: js.UndefOr[String]
val price: js.UndefOr[Int]
}
object JSItem {
implicit class JSItemOps(val self: JSItem) extends AnyVal {
def toItem: Item = {
Item(
self.name.toOption,
self.price.toOption)
}
}
}

Getting these objects from JavaScript to Scala is actually fairly easy. You were on the right track, but needed a little bit more -- the trick is that, for cases like this, you need to use js.UndefOr[T] instead of Option[T], and define it as a facade. UndefOr is a Scala.js type that means exactly "this is either a T or undefined", and is mainly intended for interaction cases like this. It includes a .toOption method, so it's easy to interface with Scala code. You can then simply cast the object you get from JavaScript to this facade type, and everything ought to work.
Creating one of these JSMegaObjects from Scala takes a bit more work. For cases like this, where you are trying to create a complex structure with lots of fields that might or might not exist, we have JSOptionBuilder. It's named like that because it was written for the big "options" objects that are common in jQuery, but it's not jQuery-specific. You can find it in the jsext library, and documentation can be found on the front page there.
You can also see a moderately complex fully-worked example in the JQueryAjaxSettings class in jquery-facade. That shows both the JQueryAjaxSettings trait (the facade for the JavaScript object) and the JQueryAjaxSettingsBuilder (which lets you construct one from scratch in Scala).

Related

Class with grouped properties in deeper levels

I'm trying to build a complex class where I want to group properties, making the instantiated object have multiple layers, instead of every property being at root level.
So far, the only way I've found to do this is by making a class with the properties to group, and then in a "parent" class add a property of the class I built.
The problem here though is that two properties not sharing the same class can't communicate with each other.
There are ways around this, but I find them all very hacky and looking bad. One would be to create a hidden element, and store data in there that a property from another class can read.
Another would be to create static properties, but then, unless you do some major work with that property, you can only have one object created from the parent class, as it'll be the same no matter the instantiation of the class.
Very basic example:
class A {
constructor(prop1){
this.property = prop1;
}
}
class B {
constructor(prop2){
this.property = prop2;
}
}
class C {
constructor(prop1, prop2){
this.PropertyA = new A(prop1);
this.PropertyB = new B(prop2);
}
}
let obj = new C(1, 1);
console.log(obj.PropertyA.property);
In this example, the property from class A can't get a value from property in class B.
So, my question is, is there another way of building the class C to keep the levels of hierarchy in the object?
I use the class structure because I like how it looks. It looks far more readable to me than the prototype structure, and I'm not building an object directly, as I would like to instantiate more of them.
It feels like I have forgotten things I've looked at to try to do this, but I'm sure it'll come to me soon enough after I post this.
Sooo...
I worked a bit on a static-solution, and basically made a private static property to hold a unique id per instantiated object, with the key-value pairs I want to be able to share between the different classes. This should only expose the methods to either set or get those values. The only requirement is that all the classes needs to be constructed with the object ID, so they can get the right value.
I understand that people will roll their eyes at my infantile tries to break the actual points of classes and such, but it works for me anyway in this specific circumstance anyway.
I'm sure there a multitude of ways to update it to ensure it runs more smoothly, but I think it works for most cases at the moment.
The code made in example code:
"use strict";
class A {
#id
#testProp
constructor(id){
this.#id = id;
this.#testProp = 10;
}
get TestProp(){ return this.#testProp + C.getSharedProp(this.#id, "BValue")};
set TestProp(newValue) { this.#testProp = newValue; C.setSharedProp(this.#id, "AValue", this.#testProp) };
}
class B {
#id
#testProp
constructor(id){
this.#id = id;
this.#testProp = 10;
}
get TestProp(){ return this.#testProp + C.getSharedProp(this.#id, "AValue")};
set TestProp(newValue) { this.#testProp = newValue; C.setSharedProp(this.#id, "BValue", this.#testProp) };
}
class C {
#id
constructor(){
this.#id = Math.random().toString(36).substr(2, 9);
this.PropertyA = new A(this.#id);
this.PropertyB = new B(this.#id);
}
static #sharedProps = {};
static getSharedProp(charId, valueName) {
if(!charId){
throw "Must supply character ID";
}
if(!valueName){
throw "Must supply name of value to return";
}
if(!(charId in this.#sharedProps)){
throw "Character ID not found";
}
if(!(valueName in this.#sharedProps[charId])){
throw valueName + "-element not found";
}
return this.#sharedProps[charId][valueName];
}
static setSharedProp(charId, valueName, value) {
if(!charId){
throw "Must supply character ID";
}
if(!valueName){
throw "Must supply name of value";
}
if(!(charId in this.#sharedProps)){
this.#sharedProps[charId] = [];
}
if(!(valueName in this.#sharedProps[charId])){
this.#sharedProps[charId][valueName] = -1;
}
if(!value){
console.warn("Value not supplied of " + valueName + ". Not updating extant value");
}else{
this.#sharedProps[charId][valueName] = value;
}
}
}
let obj = new C();
obj.PropertyA.TestProp = 20;
obj.PropertyB.TestProp = 5;
console.log(obj.PropertyA.TestProp); //should be 25; 20 from its own class and 5 from foreign class-object
console.log(obj.PropertyB.TestProp); //should be 25; 5 from its own class and 20 from foreign class-object

TypeScript class function as function VS variable which has better performace

Is it better using variables which define a function or using a function in the first place?
Furthermore, is there a difference for tree-shaking?
I have a lot of calculation (static) intensive helper classes and was wondering what the best (memory/speed) is.
Here the different ways I have in mind:
class MyClass {
readonly functionA = (v: string | number, maxDeep: number, curDeep: number = 0): string => {
if (curDeep < maxDeep) {
return this.functionA(v, maxDeep, curDeep + 1);
} else {
return "function A" + v;
}
}
static functionB(v: string | number, maxDeep: number, curDeep: number = 0): string {
if (curDeep < maxDeep) {
return MyClass.functionB(v, maxDeep, curDeep + 1);
} else {
return "function B" + v;
}
}
functionC(v: string | number, maxDeep: number, curDeep: number = 0): string {
if (curDeep < maxDeep) {
return this.functionC(v, maxDeep, curDeep + 1);
} else {
return "function C" + v;
}
}
static readonly functionD = (v: string | number, maxDeep: number, curDeep: number = 0): string => {
if (curDeep < maxDeep) {
return MyClass.functionD(v, maxDeep, curDeep + 1);
} else {
return "function D" + v;
}
}
}
I tried using JSBen for measuring a difference but the results seem to be random.
If you are that concerned about performance to optimize at this level, then having a class that only has static methods introduces some totally unnecessary overhead. Classes are designed to be instantiated, and if you don't use that feature you are wasting some computational resources to have those features available.
When I run your examples (MacOS, Chrome 90.0.4430.93) I get this:
What's clear is that static methods have a large performance costs. Where instance methods are guaranteed to be fast. I wish I could tell you why, but I'm sure it has something to do with the fact that classes are design to be instantiated.
Much simpler than that, is a simple object. Let's add these two tests:
const obj = {
functionE(v, maxDeep, curDeep = 0) {
//...
},
functionF: (v, maxDeep, curDeep = 0) => {
//...
}
};
Those run just about as fast as the instance methods. And it's a pattern that makes more sense. There's no classes because there is no instantiation.
But there's an even simpler alternative:
function rawFunctionStatementG(v, maxDeep, curDeep = 0) {
//...
}
const rawFunctionVarH = (v, maxDeep, curDeep = 0) => {
//...
};
Performance wise, we have a winner here. I'm fairly sure this is because you never have to look up a property on an object. You have a reference to the function directly, and can execute it without asking any other object where to find it first.
Updated jsben.ch
And as far as tree shaking, this form is by far the best. It's easiest for bundlers to manage whole exported values, rather than trying to slice and dice individual objects.
If you do something like:
// util-fns.ts
export function myFuncA() { /* ... */ }
export function myFuncB() { /* ... */ }
// some consumer file
import { myFuncA } from './util-fns'
Then it's super easy for a bundler to see that myFuncB is never imported or referenced, and could be potentially pruned.
Lastly...
“The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming.” – Donald Knuth
It's very unlikely for performance optimizations of this level to matter in the vast majority of javascript applications. So use the data structures that make the most sense for the application, and according to the standards of the codebase/organization. Those things matter so much more.

Javascript polymorphism without OOP classes

In JS or OOP language the polymorhpism is created by making different types.
For example:
class Field {...}
class DropdownField extends Field {
getValue() {
//implementation ....
}
}
Imagine I have library forms.js with some methods:
class Forms {
getFieldsValues() {
let values = [];
for (let f of this.fields) {
values.push(f.getValue());
}
return values;
}
}
This gets all field values. Notice the library doesnt care what field it is.
This way developer A created the library and developer B can make new fields: AutocompleterField.
He can add methods in AutocompleterField withouth changing the library code (Forms.js) .
If I use functional programming method in JS, how can I achieve this?
If I dont have methods in object i can use case statements but this violates the principle. Similar to this:
if (field.type == 'DropdownField')...
else if (field.type == 'Autocompleter')..
If developer B add new type he should change the library code.
So is there any good way to solve the issue in javascript without using object oriented programming.
I know Js isnt exactly OOP nor FP but anyway.
Thanks
JavaScript being a multi-purpose language, you can of course solve it in different ways. When switching to functional programming, the answer is really simple: Use functions! The problem with your example is this: It is so stripped down, you can do exactly the same it does with just 3 lines:
// getValue :: DOMNode -> String
const getValue = field => field.value;
// readForm :: Array DOMNode -> Array String
const readForm = formFields => formFields.map(getValue);
readForm(Array.from(document.querySelectorAll('input, textarea, select')));
// -> ['Value1', 'Value2', ... 'ValueN']
The critical thing is: How is Field::getValue() implemented, what does it return? Or more precisely: How does DropdownField::getValue() differ from AutocompleteField::getValue() and for example NumberField::getValue()? Do all of them just return the value? Do they return a pair of name and value? Do they even need to be different?
The question is therefor, do your Field classes and their inheriting classes differ because of the way their getValue() methods work or do they rather differ because of other functionality they have? For example, the "autocomplete" functionality of a textfield isn't (or shouldn't be) tied to the way the value is taken from it.
In case you really need to read the values differently, you can implement a function which takes a map/dictionary/object/POJO of {fieldtype: readerFunction} pairs:
/* Library code */
// getTextInputValue :: DOMNode -> String
const getTextInputValue = field => field.value;
// getDropdownValue :: DOMNode -> String
const getDropdownValue = field => field.options[field.selectedIndex].value;
// getTextareaValue :: DOMNode -> String
const getTextareaValue = field => field.textContent;
// readFieldsBy :: {String :: (a -> String)} -> DOMNode -> Array String
readFieldsBy = kv => form => Object.keys(kv).reduce((acc, k) => {
return acc.concat(Array.from(form.querySelectorAll(k)).map(kv[k]));
}, []);
/* Code the library consumer writes */
const readMyForm = readFieldsBy({
'input[type="text"]': getTextInputValue,
'select': getDropdownValue,
'textarea': getTextareaValue
});
readMyForm(document.querySelector('#myform'));
// -> ['Value1', 'Value2', ... 'ValueN']
Note: I intentionally didn't mention things like the IO monad here, because it would make stuff more complicated, but you might want to look it up.
In JS or OOP language the polymorhpism is created by making different types.
Yes. Or rather, by implementing the same type interface in different objects.
How can I use Javascript polymorphism without OOP classes
You seem to confuse classes with types here. You don't need JS class syntax to create objects at all.
You can just have
const autocompleteField = {
getValue() {
…
}
};
const dropdownField = {
getValue() {
…
}
};
and use the two in your Forms instance.
Depends on what you mean by "polymorphism". There's the so-called ad-hoc polymorphism which type classes in Haskell, Scala, or PureScript provide -- and this kind of dispatch is usually implemented by passing witness objects along as additional function arguments, which then will know how to perform the polymorphic functionality.
For example, the following PureScript code (from the docs), which provides a show function for some types:
class Show a where
show :: a -> String
instance showString :: Show String where
show s = s
instance showBoolean :: Show Boolean where
show true = "true"
show false = "false"
instance showArray :: (Show a) => Show (Array a) where
show xs = "[" <> joinWith ", " (map show xs) <> "]"
example = show [true, false]
It gets compiled to the following JS (which I shortened):
var Show = function (show) {
this.show = show;
};
var show = function (dict) {
return dict.show;
};
var showString = new Show(function (s) {
return s;
});
var showBoolean = new Show(function (v) {
if (v) {
return "true";
};
if (!v) {
return "false";
};
throw new Error("Failed pattern match at Main line 12, column 1 - line 12, column 37: " + [ v.constructor.name ]);
});
var showArray = function (dictShow) {
return new Show(function (xs) {
return "[" + (Data_String.joinWith(", ")(Data_Functor.map(Data_Functor.functorArray)(show(dictShow))(xs)) + "]");
});
};
var example = show(showArray(showBoolean))([ true, false ]);
There's absolutely no magic here, just some additional arguments. And at the "top", where you actually know concrete types, you have to pass in the matching concrete witness objects.
In your case, you would pass around something like a HasValue witness for different forms.
You could use a the factory pattern to ensure you follow the open close principle.
This principle says "Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification".
class FieldValueProviderFactory {
getFieldValue(field) {
return this.providers.find(p => p.type === field.type).provider(field);
}
registerProvider(type, provider) {
if(!this.providers) {
this.providers = [];
}
this.providers.push({type:type, provider:provider});
}
}
var provider = new FieldValueProviderFactory();
provider.registerProvider('DropdownField', (field) => [ 1, 2, 3 ]);
provider.registerProvider('Autocompleter', (field) => [ 3, 2, 1 ]);
class FieldCollection {
getFieldsValues() {
this.fields = [ { type:'DropdownField',value:'1' }, { type:'Autocompleter',value:'2' } ];
let values = [];
for (let field of this.fields) {
values.push(provider.getFieldValue(field));
}
return values;
}
}
Now when you want to register new field types you can register a provider for them in the factory and don't have to modify your field code.
new Field().getFieldsValues();

Finding elements in an array of objects where you have a bigger key

I'm writing a tiny reactive framework where I need to find out which subscriber needs updating. I'm implementing deep binding and I'm running into a wall how to find subscribers in an effective manner.
A stored variable can be an object, so for example
{
"user": {
"preferences": {
"food": "vegetarian"
}
}
}
You can get content to any level of this variable like this
getVar("user_preferences_food");
getVar("user_preferences");
However, you can also update it like that
setVar("user_preferences_food", "meat");
setVar("user_preferences", {"food": "meat"});
But in case of the first setVar (user_preferences_food) how can I find the subscriber using getVar("user_preferences"); or even getVar("user"); most effectively.
I already got it working by splitting the var on _ and then one by one concatting the next level and merging all the resulting arrays. But this is very resource intensive. Especially if there are a lot of subscribers. There must be a better way to find them that is less resource intensive.
Edit: I left out part of the explanation.
There is a subscribe method too
subscribe("user", cb);
subscribe("user_preferences", cb);
subscribe("user_preferences_food", cb);
These subscriptions are stored in an array in the framework.
As soon as "user_preferences_food" is updated for example, all subscriptions above should be triggered. But obviously not subscribe('othervar');
simplification of the subscribe method:
var subscriptions = [];
function subscribe(var, callback){
subscriptions.push({var: var, cb: callback});
}
Simplification of getVar
vars = {};
getVar(var){
// find var in vars with this logic: https://stackoverflow.com/a/18937118/249710
// current exact match on subscribers, but need the "parents, grandparents etc here
var toUpdate = _.where(subscriptions, {
"var" : var
});
_.each(toUpdate, function(sub){ sub.cb();});
}
Storing or getting data as part of the key I've already got covered. It is just finding the subscribers in the most effective manner
ps: this is in an environment where I cannot rely on ES6 yet (not all users have it enabled), there is no DOM but I do have underscore included. (Titanium app development platform)
I would try to make a list for the callbacks, so you loop trough one list so you dont have to search, because you know the list is there with all the callbacks.
So if you call setVar('user_prefs') you set a seperate list with the root var. in this case its the user.
if any object is changed with setVar (in depth or not) you go to you're root var, get the list and loop trough this list with the callbacks.
The beauty of this is you can set a list with the root var,
var cbList[FIRSTVAR] this contains all the callbacks. No searching just loop.
Its the mongoDb principle, the data is ready to go, you don't search because you know the list is already there.
You could split the string and use it for reduceing the object.
function getVar(object, path) {
return path
.split('_')
.reduce(function (o, k) {
return (o || {})[k];
}, object);
}
function setVar(object, path, value) {
var keys = path.split('_'),
last = keys.pop();
keys.reduce(function (o, k) {
return o[k] = o[k] || {};
}, object)[last] = value;
}
var object = { user: { preferences: { food: "vegetarian" } } };
console.log(getVar(object, "user_preferences_food"));
console.log(getVar(object, "user_preferences"));
setVar(object, "user_preferences_food", "meat");
console.log(object);
setVar(object, "user_preferences", {"food": "meat"});
console.log(object);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I ended up doing this:
var options = [];
var parts = key.split('_');
var string = parts[0];
_.each(parts, function(p, i){
if (i > 0) string += '_' + p;
options.push(string);
});
var toUpdate = _.filter(subscribers, function(sub){
if (sub.var.indexOf(key + '_') === 0) return true;
if (options.indexOf(sub.var) > -1) return true;
return false;
});
So checking with indexOf on the string to see if there are children. And building an array with parents so any layer is a match, and doing an indexOf on that as well. I think this is the least complicated method of implementing it

Returning an item of type T from a list of classes that inherit from a base type - Typescript

I have a class (Foo) that is used to hold a list of items, the list of items of which all inherit from a base type (IBar), this list can contain any number of these items.
The issue I have is that I am trying to create a get method on Foo that takes a generic type restricted to types that inherit from IBar.
The interfaces and classes I have currently are:
interface IBar {
bar : string;
}
interface IFizz extends IBar {
buzz : string;
}
class Foo {
get<T extends IBar>() : T {
var item = this.list[0];
return item;
}
list : Array<IBar>;
}
With the code i'm trying to run being:
var foo = new Foo();
var item = foo.get<IFizz>();
I know in the above that the list is empty, but this is more trying to get the Typescript compiler not to show an error. The line that calls foo.get is fine and does not error, the problem is the get method itself.
The error i get from the above is "Type 'IBar' is not assignable to type 'T'".
If looking at C# for a reference the above would work (I believe) and would welcome any written examples to help me solve this.
Thanks
The above code would not work in C# either. Both language try to prevent you to do this, because this is not type safe. Consider the following code:
class Foo {
get<T extends IBar>() : T {
var item = this.list[0];
return item;
}
list : Array<IBar> = [ new OneClass() ];
}
var foo = new Foo();
var item = foo.get<AnotherClass>();
OneClass and AnotherClass both implement IBar, but list[0] is of type OneClass but the caller requests an instance of AnotherClass. So if the compiler would allow the code you wrote, you get item typed as AnotherClass but holding an instance of OneClass which may cause runtime errors.
Just like in C# you can force the typescript compiler to let you do this using a type assertion (or similarly a cast in C#). Although you can do this, you should have another mechanism for ensuring that the item in the array is actually of a correct type for T:
class Foo {
get<T extends IBar>() : T {
var item = this.list[0];
return item as T;
}
list : Array<IBar> = [];
}
var foo = new Foo();
var item = foo.get<IFizz>();
You're trying to assert that the item at index 0 is an IFizz, not just an IBar; that can't be assured statically given Foo's declaration, it can only be true at runtime with specific data. Although you could assert it (return item as T;), that's just hiding the problem: When code actually runs, the item at index 0 may not be an IFizz.
If the list is going to contain IFizz, it should be declared to do so. If it's going to contain any kind of IBar, it should be declared to do that, and any code using it for IFizz instances is responsible for ensuring that's really true.
Instead, Foo should be parameterized with the actual type of IBar it will contain:
class Foo<T extends IBar> {
get() : T {
var item = this.list[0];
return item;
}
list : Array<T>;
}
and then
var item = foo.get();

Categories