How does redux tookit query create methods dynamically in typescript? - redux-toolkit

I need to do the same thing that redux toolkit query does for creating dynamic methods with types, in typescript. Like in this example:
export const pokemonApi = createApi({
reducerPath: 'pokemonApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
endpoints: (builder) => ({
getPokemonByName: builder.query<Pokemon, string>({
query: (name) => `pokemon/${name}`,
}),
}),
})
// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
export const { useGetPokemonByNameQuery } = pokemonApi
typescript validates that useGetPokemonByNameQuery is a function of pokemonApi and gets the right parameters too.

It's a combination of Mapped Types and Template Type Literals using Key Remapping via
as.
The code in question:
export type HooksWithUniqueNames<Definitions extends EndpointDefinitions> =
keyof Definitions extends infer Keys
? Keys extends string
? Definitions[Keys] extends { type: DefinitionType.query }
? {
[K in Keys as `use${Capitalize<K>}Query`]: UseQuery<
Extract<Definitions[K], QueryDefinition<any, any, any, any>>
>
} &
{
[K in Keys as `useLazy${Capitalize<K>}Query`]: UseLazyQuery<
Extract<Definitions[K], QueryDefinition<any, any, any, any>>
>
}
: Definitions[Keys] extends { type: DefinitionType.mutation }
? {
[K in Keys as `use${Capitalize<K>}Mutation`]: UseMutation<
Extract<Definitions[K], MutationDefinition<any, any, any, any>>
>
}
: never
: never
: never

Related

Using State with HTMLElement with createSlice from 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

Flutter - Dart - Reflectable package : how to: get class fields type? instantiate object from class/Type? set fields values using Reflectable

I came to know that dart mirrors is disabled in flutter but hope that there might be some alternate way to achieve. Mirrors must not be disabled in flutter, it is an important & must have feature.
import 'package:reflectable/mirrors.dart';
import 'package:reflectable/reflectable.dart';
const reflector = const Reflector();
class Reflector extends Reflectable
{
const Reflector() : super(
invokingCapability,
typingCapability,
reflectedTypeCapability,
);
}
#reflector
class Dictionary
{
String english, myLang;
int index;
}
main() {
test();
}
test()
{
ClassMirror classMirror = reflector.reflectType(Dictionary);
classMirror.declarations.values.forEach((field)
{
VariableMirror variableMirror = field;
/*??????????????????????????????????????????
Now How To Get Field types i.e. String & int
How to instantiate class object
How to set fields values
???????????????????????????????????????????*/
});
}
Runtime object instantiation:
Use the method newInstance from ClassMirror. The first argument is the constructor name. Like you haven't name constructors, simple pass an empty string. The second argument are an array of positional constructor arguments. No constructor, empty array.
Dictionary dic = classMirror.newInstance("", []);
Set fields values:
Use the method invokeSetter from InstanceMirror. The first argument is the field name and the second is the value.
InstanceMirror instanceMirror = reflector.reflect(dic);
instanceMirror.invokeSetter("index", 3);
So far I am able to know field(s) type:
ClassMirror classMirror = reflector.reflectType(Dictionary);
VariableMirror variableMirror = classMirror.declarations["english"] as VariableMirror;
Type type = variableMirror.dynamicReflectedType;
print("field: " + variableMirror.simpleName + " has type: " + type.toString());
Now remaining:
Runtime object instantiation?
How to set fields value of instantiated object?

How to make an parametrized enum build macro?

Now Solved
I want to build an enum with a macro, including defining its type parameters.
There are a couple of sources describing adding enum fields with macros , but I havent found any which describe how to build an enum with specified parameter types using macros. There is a documentation entry made for limitations of macros here regarding parameter types, but that is still left empty.
The idea is to use a macro to generate a specified number of Either enums with increasing amount of parameter types.
//Either.hx
#:build(macros.build.EitherBuildMacro.build(10))
// enum Either {} <- this isnt sufficient as we need to generated several
// enums (in this example 10 of them) with parameter types...
//And it should generate
enum Either2<A,B>{
_1(value:A);
_2(value:B);
}
enum Either3<A,B,C>{
_1(value:A);
_2(value:B);
_3(value:C);
}
enum Either4<A,B,C,D>{
_1(value:A);
_2(value:B);
_3(value:C);
_4(value:D);
}
//etc until enum Either10<A,B,C,D,E,F,G,H,I,J>
As I showed earlier in this post there is an article describing how to add fields, but not types. I am clueless how to set these parameter types by a macro and it seems like there are some limitations, yet undocumented. Any pointers which command to use for that are highly appreciated. Defining series of Enums with increasing parameterization is typically something you rather want to do with build macros, than to do by hand. Especially since you could pare each macro generated EitherN with a macro generated OneOfN abstract
abstract OneOf2<A, B>(Either<A, B>) from Either<A, B> to Either<A, B> {
#:from inline static function fromA<A, B>(value:A):OneOf<A, B> {
return _1(a);
}
#:from inline static function fromB<A, B>(value:B):OneOf<A, B> {
return _2(b);
}
#:to inline function toA():Null<A> return switch(this) {
case _1(value): value;
default: null;
}
#:to inline function toB():Null<B> return switch(this) {
case _2(value): value;
default: null;
}
}
abstract OneOf3<A, B, C>(Either<A, B, C>) from Either<A, B, C> to Either<A, B, C> {
#:from inline static function fromA<A, B, C>(value:A):OneOf<A, B, C> {
return _1(value);
}
#:from inline static function fromB<A, B, C>(value:B):OneOf<A, B, C> {
return _2(value);
}
#:from inline static function fromC<A, B, C>(value:C):OneOf<A, B, C> {
return _3(value);
}
#:to inline function toA():Null<A> return switch(this) {
case _1(value): value;
default: null;
}
#:to inline function toB():Null<B> return switch(this) {
case _2(value): value;
default: null;
}
#:to inline function toC():Null<C> return switch(this) {
case _3(value): value;
default: null;
}
}
//etc
The same idea would be handy to generate series of Tuples and Functions with increasing amount of parameter types. Would be a efficient and flexible way to generate the right amount of enums, abstracts and typedefs
#:build() indeed isn't the right approach here, since that just builds one particular type. Instead, you could use an initialization macro in combination with Context.defineType():
--macro Macro.init()
import haxe.macro.Context;
class Macro {
public static function init() {
for (i in 2...11) {
Context.defineType({
pack: [],
name: "Either" + i,
pos: Context.currentPos(),
kind: TDEnum,
fields: [
for (j in 0...i) {
name: "_" + (j + 1),
kind: FFun({
args: [
{
name: "value",
type: TPath({
name: String.fromCharCode(65 + j),
pack: []
})
}
],
ret: null,
expr: null
}),
pos: Context.currentPos()
}
],
params: [
for (j in 0...i) {
name: String.fromCharCode(65 + j)
}
]
});
}
}
}
With -D dump=pretty you can see that this generates Either2-10:
With for instance Either2.dump looking like this:
#:used
enum Either2<A : Either2.A,B : Either2.B> {
_1(value:Either2.A);
_2(value:Either2.B);
}
Alternatively, you could consider using #:genericBuild() in combination with a Rest type parameter. That would essentially do the same and still use Context.defineType(), with a few advantges:
it would allow you to avoid encoding the number of type parameters into the type name (so it would just be Either instead of Either2 / 3 / etc)
the amount of type parameters would not be limited to an arbitrary amount such as 10
types would only be generated "on demand"
You can find an example here.

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.

Typescript : is there a way to distinguish classes with same structures?

For sure this kind of stuff could be implemented in some other way but I'm still curious why TS doesn't warn. Same structures could have different data.
class ComponentContainer<TComponent> extends Array<TComponent> {}
class Context<TComponent> extends Array<ComponentContainer<TComponent>> {}
///////////////////////////////////////////////////////
class Component<T> { constructor(public value: T) {} }
class Movable extends Component<boolean> {
constructor(value: boolean = true) { super(value) }
}
class Static extends Component<boolean> {
constructor(value: boolean = true) { super(value) }
}
///////////////////////////////////////////////////////
const ctx: Context<Movable> = new Context()
ctx[0].push(new Static()) // <-- No error
EDIT
Could be a beginning of an alternative here :https://basarat.gitbooks.io/typescript/docs/tips/nominalTyping.html
Types don't presume that value should be an instance of specified class. Movable class is used as interface here:
const ctx: Context<Movable> = new Context()
If a value should be an instance of Movable class, this check is usually performed at run time with if (value instanceof Movable). It will be tricky to implement in Array subclass, it may be better for Context to implement its own API that allows to validate values instead of extending Array.
Same structures could have different data
But they don't have different data. Static conforms to Movable interface, so it doesn't cause type error. Similarly, this will work, because the object conforms to this interface:
ctx[0].push({ value: true })
And will cause TypeScript type error if it doesn't:
ctx[0].push({ value: 1 })
It's possible to additionally enforce the value to be an instance of Movable through type checks with merged interface:
interface Movable {
constructor: typeof Movable
}
class Movable extends Component<boolean> {
constructor(value: boolean = true) { super(value) }
}
It still will be possible to cheat typing system with:
ctx[0].push({ constructor: Movable, value: 1 });
After playing a bit with your code in TypeScript Playground, it seems like TypeScript is smart enough to detect that Movable and Static are basically the same type besides their name. They are both a Component<boolean> and have no properties, so it allows you to push a new Static instance into a Movable array. Only when I added a property to Movable which didn't exist in Static then TypeScript compiler showed an error, for example:
I guess that's just how TypeScript works. It doesn't necessarily prohibit you from using different types on a generic object unless the type that's given is missing properties which exist on the expected type. That's why the following also works: