Using State with HTMLElement with createSlice from Redux Toolkit - redux-toolkit

When trying to create a reducer whose state contains a key that is of type HTMLDivElement (or any other HTMLElement derivative), I get an Argument of type '...' is not assignable to parameter of type for what seems to be all of the keys of the HTMLElement.
interface ITestEvent {
name: string;
id: string;
ref: HTMLDivElement;
}
interface AddEventPayload {
event: ITestEvent;
}
interface TestEventState {
events: ITestEvent[];
}
const initialState: TestEventState = {
events: [],
};
const testSlice = createSlice({
name: 'test',
initialState,
reducers: {
addEvent(state, action: PayloadAction<AddEventPayload>) {
state.events.push(action.payload.event); // Error here
},
},
});
Below is a typescript playground link illustrating the issue.
https://typescriptlang.org/play link here
The same happens when creating a reducer via createReducer.
Is this a limitation of TypeScript in some way/is this expected?

This looks to be an issue with the immer type Draft.
Draft<typeof initialState> seems to end up with something slightly different than the original Event type in there.
But generally, this is advised against and will probably also lead to runtime issues and maybe even the redux devtools crashing when they try to display that event.
It is only ever advised to put serializable normal objects into the state:
https://redux.js.org/style-guide/style-guide/#do-not-put-non-serializable-values-in-state-or-actions

Related

AWS-CDK Appsync Codefirst input types

To avoid duplication of data structures I wanted to reuse a type definition on an input type like this
export const DeviceStatus = new ObjectType('DeviceStatus', {
definition: {
time: timestamp,
firmwareVersion: string
},
});
export const DeviceStatusInput = new InputType('DeviceStatusInput', {
definition: {
tenantId: id_required,
deviceId: id_required,
// Reuse of DeviceStatus Field definition
status: DeviceStatus.attribute()
}
});
There is no error since the return type of DeviceStatus.attribute() is fine, and this works for ObjectType inheritance.
From my perspective this should work, but deploying results in a nasty "Internal Error creating Schema" error.
Of course I could move the whole definition into an object and reuse it but that seems weird. Is there any good solution on this for the CodeFirst approach
It seem to be invalid to reference object type in input type.
I recommend to view Can you make a graphql type both an input and output type?
Probably best you can do is to create some convenience method which will create you both object and input type from single definition.

Get semantic object and semantic action in controller

I'm trying to get the semantic object and semantic action of my deployed SAPUI5 application. I tried looking into ushell services - URLParsing and LaunchPage but it does not seem to return my semantic objects and actions.
Have anybody tried this?
This worked for me so far:
sap.ui.require([ // modules lazily instead of accessing them with global names.
"sap/ushell/library", // Consider adding `"sap.ushell": { lazy: true }` to dependencies in manifest.json
"sap/ui/core/routing/HashChanger",
], async (sapUshellLib, HashChanger) => {
const fullHash = new HashChanger().getHash(); // returns e.g. "masterDetail-display?sap-ui-theme=sap_fiori_3&/product/HT-1000"
const urlParsing = await sapUshellLib.Container.getServiceAsync("URLParsing");
urlParsing.parseShellHash(fullHash); /** returns e.g. {
action: "display",
appSpecificRoute: "&/product/HT-1000",
contextRaw: undefined,
params: { "sap-ui-theme": "sap_fiori_3" },
semanticObject: "masterDetail"
} **/
});
You can always just use
window.location.hash
Which you can parse yourself pretty easily. If you really want launchpad code, you can often find it here:
sap.ushell.services.AppConfiguration.getCurrentApplication().sShellHash
I've noticed it's not always set though when you're looking at an embedded component
A simplistic way to do this would be:
var oHashObject = new sap.ui.core.routing.HashChanger();
oHashObject.getHash();
//this will return the semantic object and action alongwith the routing params

Using local variables in function built with haxe macro

I have a LangBuilder macro class; it's used to build a langObjects:Map<String, Dynamic> of texts in various languages at compile time, and inject this structure in classes via #:build macro. Every item of the Map has a field for every language supported. So the result is:
#:build(LangBuilder.build())
class Lang{}
trace(Lang.langObjects["hello-world"].en); //outputs "Hello World!"
trace(Lang.langObjects["hello-world"].it); //outputs "Ciao Mondo!"
This works perfectly, but I thought I could make a cleaner job hiding the langObjects structure using a function getLangText with arguments the id of the text (e.g. "hello-world") and the language code (e.g. "it").
So I'm trying to add this function to classes:
public static function getLangText(id:String, lang:String)
Its non-macro version could be expressed as:
public static function getLangText(id:String, lang:String){
var _langObj_id = langObjects[id];
switch(lang){
case "it":
return _langObj_id.it;
case "en":
return _langObj_id.en;
}
return "Missing Translation";
If i translate this function as a macro with this code:
var code = macro {
var _langObj_id = langObjects[$i{"id"}];
switch($i{"lang"}){
case "it":
return _langObj_id.it;
case "en":
return _langObj_id.en;
}
return "Missing translation";
};
var myFunc:Function = {
args: [{
name: "id",
type: TPath({name: "String", pack: []}),
meta: null
},
{
name: "lang",
type: TPath({name: "String", pack: []}),
meta: null
}],
ret: (macro:String),
expr: macro $code
};
fields.push({
pos: Context.currentPos(),
name: "getLangText",
meta: null,
kind: FieldType.FFun(myFunc),
doc: null,
access: [Access.APublic, Access.AStatic]
});
... it works without problems. However I would like to know how it could be written without the switch, to make it more flexible and to learn something about haxe macros. I have seen some examples where fields could be accessed in macros with $p{} or with object.$fieldName. However the haxe manual warns that the second form could be used only for simple identifiers; for example object.${fieldName} would not work.
So I try this code:
var code = macro {
var l:String = $i{"lang"};
var _langObj_id = langObjects[$i{"id"}];
return _langObj_id.$l;
};
The compiler gives an error
Unknown identifier : l
on the line containing return _langObj_id.$l;.
Then i tried to use the $p{} reification:
var code = macro {
var _langObj_id = langObjects[$i{"id"}];
return macro $p{["_langObj_id", $i{"lang"}]};
};
But the error is similar:
Unknown identifier : lang
I can surely change the langObjects structure to Map<String, Map<String, String>> and then change the code to:
var code = macro {
return macro langObjects[$i{"id"}][$i{"lang"}];
};
I think this would work, but now i'm trying to understand why both _langObj_id.$lang and $p{["_langObj_id", $i{"lang"}]} wouldn't work, and what would be the correct way to access a field in a situation like that.
The value of the lang parameter is not known at compile- / macro-time, so I don't see how you could generate a field access expression like langObjects["mytext"].en. At runtime when getLangText() is actually called, lang could be "en", or anything else. So that would still require a switch-case, if-else-chain or reflection to handle all the possible values.
If instead of using being created by a build macro, getLangText() was an expression macro / a macro function, the function call would be evaluated at compile-time, and be replaced with the expression it returns. That would allow you to generate the appropriate field access expression based on the parameters. It could look something like this:
class Macro {
public static var langObjects = ["mytext" => {en: "hello", de: "hallo"}];
public static macro function getLangText(id:String, lang:String) {
return macro {
var langObject = Macro.langObjects[$v{id}];
langObject.$lang;
}
}
}
class Main {
static function main() {
trace(Macro.getLangText("mytext", "en"));
trace(Macro.getLangText("mytext", "de"));
}
}
Compiles to the following on the JS target:
Main.main = function() {
var langObject = Macro.langObjects.get("mytext");
console.log("source/Main.hx:3:",langObject.en);
var langObject1 = Macro.langObjects.get("mytext");
console.log("source/Main.hx:4:",langObject1.de);
};
Perhaps that's what you're looking for? Hard to say without knowing what problem you're trying to solve.

Does Typescript support "subset types"?

Let's say I have an interface:
interface IUser {
email: string;
id: number;
phone: string;
};
Then I have a function that expects a subset (or complete match) of that type. Maybe it will pass an entire object, maybe it will just pass in {email: "t#g.com"}. I want the type checker to allow for both.
Example:
function updateUser(user: IUser) {
// Update a "subset" of user attributes:
$http.put("/users/update", user);
}
Does Typescript support this sort of behavior yet? I could find it very useful, particularly with paradigms like Redux.
To clarify, the goal is:
Avoid re-writing an interface and manually setting all attributes to optional.
Avoid assignment of unexpected attributes (such as spelling mistakes).
Avoid imperative logic such as if statements, which forfeit benefits of compile time type checking.
UPDATE: Typescript has announced support for mapped types which should solve this problem once published.
It's worth noting that Partial<T>, as suggested in the accepted answer, makes all fields optional, which is not necessarily what you need.
If you want to make some fields required (e.g. id and email), you need to combine it with Pick:
type UserWithOptionalPhone = Pick<IUser, 'id' | 'email'> & Partial<IUser>
Some explanation:
What Pick does is that it lets you specify a subset of the interface succinctly (without creating a whole new interface repeating the field types, as suggested by other answers), and then lets you use those, and only those fields.
function hello1(user: Pick<IUser, 'id' | 'email'>) {
}
hello1({email: '#', id: 1}); //OK
hello1({email: '#'}); //Not OK, id missing
hello1({email: '#', id: 1, phone: '123'}); //Not OK, phone not allowed
Now, this is not exactly what we need, as we want to allow, but not require phone. To do that, we "merge" the partial and the "picked" version of our type by creating an intersection type, which then will have id and email as required fields, and everything else as optional – exactly how we wanted it.
function hello2(user: Pick<IUser, 'id' | 'email'> & Partial<IUser>) {
}
hello2({email: '#', id: 1}); //OK
hello2({email: '#', id: 1, phone: '123'}); //OK
hello2({email: '#'}); //Not OK, id missing
Typescript now supports partial types.
The correct way to create a partial type is:
type PartialUser = Partial<IUser>;
What you want is this
type Subset<T extends U, U> = U;
this makes sure, that U is a subset of T and returns U as a new type. for example:
interface Foo {
name: string;
age: number;
}
type Bar = Subset<Foo, {
name: string;
}>;
you can not add new properties to Bar which are not part of Foo - and you can not alter types in a non-compatible way. this also works recursively on nested objects.
proper solution with mapped types:
updateUser<K extends keyof IUser>(userData: {[P in K]: IUser[P]}) {
...
}
You can declare some or all fields as optional fields.
interface IUser {
email: string; // not optional
id?: number; // optional
phone?: string; // optional
};
You can seperate it into different interfaces:
interface IUser {
id: number;
};
interface IUserEmail extends IUser {
email: string;
}
interface IUserPhone extends IUser {
phone: string;
}
Have your method receive the base IUser interface and then check for the fields you need:
function doit(user: IUser) {
if (user.email) {
} else if (user.phone) {
}
}
If I understand this question correctly, you want something like Flow's $Shape
So, in one place, you may have something that requires the type
interface IUser {
email: string;
id: number;
phone: string;
};
Then, in another place you want a the type with the same type as IUser just with all the fields now optional.
interface IUserOptional {
email?: string;
id?: number;
phone?: string;
};
You want a way to auto-generate IUserOptional based on IUser without having to write out the types again.
Now, I don't think this is possible in Typescript. Things may change in 2.0, but I don't think we're even close to something like this in Typescript yet.
You could look at a pre-compiler which would generate such code for you before typescript runs, but that doesn't sound like a trivial thing to do.
With this problem in mind, I can only suggest you try Flow instead. In flow you can just do $Shape<IUser> to generate the type you want programmatically. Of course, Flow differs from Typescript in many big and small ways, so keep that in mind. Flow is not a compiler, so you won't get things like Enums and class implementing interfactes

MooTools Class objects and 'this'

Let's say I had this class.
BucketList = new Class({
Implements: [Options, Events],
options: {
items: [],
onItemAddedOrDeleted: null
},
initialize: function(options, init) {
this.setOptions(options);
this.options.onItemAddedOrDeleted();
}
});
How can I get this to work?
new BucketList([], function() {
alert(this.items.length);
});
Once instantiated, the new BucketList should alert the length of the array I passed into its constructor.
A couple of issues here. One is that you're implementing Events, so the onItemAddedOrDeleted option becomes a Class instance event (see the docs for setOptions). As a result, you can't call onItemAddedOrDeleted as a normal function, as it becomes an event listener waiting for you to trigger an event of "itemAddedOrDeleted".
Two, your syntax for passing in the function as part of the options is slightly off, as you need to pass the custom init function as part of the options object. I've reworked your code slightly to use fireEvent instead of calling the function directly, but if you wanted to call it directly instead, you could just rename it to not use the event syntax (ie. start with 'on'). This works though:
BucketList = new Class({
Implements: [Options, Events],
options: {
items: [],
onItemAddedOrDeleted: null
},
initialize: function(options) {
this.setOptions(options);
this.fireEvent('itemAddedOrDeleted');
}
});
new BucketList({items:[],onItemAddedOrDeleted:function() {
alert(this.options.items.length);
}});
Note that I surrounded the function being passed to the BucketList constructor as part of the options object.
You could do it without utilizing the Event syntax this way:
BucketList = new Class({
Implements: [Options, Events],
options: {
items: [],
itemAddedOrDeleted: null
},
initialize: function(options) {
this.setOptions(options);
this.options.itemAddedOrDeleted();
}
});
var x = new BucketList({items:['x'],itemAddedOrDeleted:function() {
alert(this.items.length);
}});