Why would it matter where a type declaration is located? - reactjs

The documentation for ReasonReact specifies where in the code a type should be declared:
The state type could be anything! An int, a string, a ref or the common record type, which you should declare right before the reducerComponent call:
type state = {counter: int, showPopUp: bool};
let component = ReasonReact.reducerComponent "Dialog";
The emphasis is theirs.
Why would it matter where the type declaration is located, so long as it's valid? Does some kind of optimization take place only when the two lines are adjacent? What happens if I insert other things between them or put them in separate modules?

The type needs to be defined before it is used, but it doesn't matter in any technical sense whether there's anything in between. That's just convention, to keep related things together.
I'll see about getting this clarified in the docs.

Putting the state type (or the retainedProps type or the action type) after the component definition will give you a type error; if you turn on super-errors (like so: https://github.com/reasonml-community/bs-glob/blob/d891ce1fadd3f3b2938d5900eb15241be4a3c1d0/bsconfig.json#L3) then the error briefly explains itself.
In short, it's a corner-case typing issue (scope escape), whose explanations you can find elsewhere.

Related

How to get helpful intellisense when using a type alias for a string union type as a property of an object or interface?

At work, I'm involved in maintaining our React component library, which we're very slowly in the process of converting to TypeScript. (But my question is about Typescript more generally and not specific to React - that's just how the question has arisen and how I imagine it might arise for many others!)
Like many such component libraries, we often use a union of string literals as the type for some props, to ensure they always have one of a small number of approved values - a typical example might be type Size = "small" | "medium" | "large";. And while we don't always use a type alias for them as shown there, it is frequent enough that we do, as the type may need to be referred to in a few different places, and it's nice (particularly with larger unions than this) to not have to type out the same thing all the time, as well as knowing that if the design team ever want us to add a new extra-large size or whatever, the type at least will only have to be updated in one place.
This works fine of course, but we've discovered that in terms of intellisense it leaves quite a lot to be desired. We want our consumers, who likely don't know all the valid values off my heart, to be told what these are by their IDE when rendering the component. But by doing this in the most obvious way - ie like this:
type Size = "small" | "medium" | "large";
interface Props {
size: Size;
}
then when consuming the props in any form, IDEs such as VSCode on hovering over the size prop will simply display the rather unhelpful Size as the prop's type, rather than the explicit union of 3 strings which would be far more helpful.
(Note that although the above is using an interface rather than a type alias, which is the way we've decided to go after some debate, the same issue is present when using type for
the props type.)
This seems like it should be a common question, but many searches on google and Stack Overflow have failed to turn anything up that's specific to simple unions of strings.
There are discussions about ways to get TS to "expand" or "simplify" complex types - like this Stack Overflow question and its great answers, or this article, which basically show the same solution although they differ in details. But these seem to be aimed at - and certainly work on - object types specifically. Sadly, no matter which of these transformations is applied to the type of the size prop in the above example, TypeScript still stubbornly shows the unhelpful Size as the prop's type when actually consuming it. (For those for whom this makes sense - Haskell is among my favourite languages - I would phrase this more succinctly as: these solutions appear to work on product types but not on sum types.)
This is demonstrated in this TS playground example - specifically the size2 prop. (This shows only one form of the Expand type, but I've tried every slight variation I've either found online or have come up with myself, with no success.)
The others - size3 and size4 - are attempts at using template literal types based on the same "trick" that is behind the Expand example. I understand they're based on using a conditional type to force distribution of the operation across a union and then making sure the operation is essentially a no-op, so the actual type is still the same but after hopefully forcing TS to compute something across the union to output a "new", "plain" union. Since the Expand type suggested above iterates across keys, fine for an object type with properties but unclear if it has any meaning for a union of string literals, it seemed using a template literal as the operation in this trick was the same idea but adapted to such a union of literal string types. And specifically, concatenating with an empty string is the only obvious way to make this keep the strings as they were.
That is, I thought
type NoOpTemplate<T> = T extends string ? `${T}${""}` : T;
might work. But as you will see from the playground link above (size3 prop), it doesn't.
I even hit upon the idea of using a generic type with two parameters:
type TemplateWithTwoParams<T, U extends string> = T extends string ? `${T}${U}` : T;
This "works" in a sense, because the prop defined as notQuiteSize: TemplateWithTwoParams<Size, "x">; displays as an explicit union as desired: "smallx" | "mediumx" | "largex". So surely supplying an empty string as the U parameter will do what we want here - keep the strings in the union the same, while forcing explicit display of the options?
Well no, it doesn't, as you can see from the size4 prop in the example! It seems the TS compiler is just too clever, as all I can assume is that it spots that in this case we're just concatenating with an empty string, which is a no-op, and therefore doesn't need to actually compute anything and thus outputs the same type T as it was given - even via the type alias.
I'm out of ideas now, and surprised this doesn't seem to be a common problem with clever solutions like the above Expand that I can read about online. So I'm asking about it now! How can we force TS to display a union type as an explicit union, when used as part of an object or interface, while still keeping the convenience of using an alias for it?
It turns out that I was thinking along the right lines, I just needed a different "do-nothing" operation to apply to each member of the union.
Specifically, wrapping the value in an object, and then extracting it, like this:
type WrapInObject<T> = T extends any ? { key: T } : never;
type Unwrap<T> = T extends { key: any } ? T["key"] : never;
type ExplicitUnion<T> = Unwrap<WrapInObject<T>>;
works as intended. Here's the TS playground example from the question expanded with this version - as prop sizeThatWorks, where if you hover to see the intellisense you will see the desired output of the explicit union, rather than the Size alias.
This has a further advantage of presumably working for any union, not just one of string literals. I wish I knew this would continue to work in future versions of the TS compiler - I hate to think that future versions may be "clever" enough to realise that this wrapping-and-unwrapping is a no-op, as apparently happened with my attempts using template literal types. But this seems to be the best available, at least at present.

Should const be outside function components in React.js?

Some of my code got a review comment saying something like "move const outside the function to avoid redeclaration". This was a normal function component, something like this:
export default function someComponent() {
const someString = 'A string';
///...
}
I was confused about the idea of this causing a redeclaration, because it does not, I know that the record that holds the variables and constants belong to the scope, so it's not exactly that.
But then i remembered that typescript does not allows you to have a const inside a class, not sure about the reason or is this is related. But then ts added the readonly modifier in ts v2, so the confusion remains.
Should const be outside function components, or not?
I would love to know more opinions.
There are 2 sides to the coin. Firstly, in terms of clean code and readability, I strongly prefer local declarations like in your example. I would love using more nested functions as well.
However, in JavaScript, every time the function executes, the local definitions will be redeclared, even if they are constants or functions. So it's a trade-off. If the function is called many times, this becomes an overhead.
I think it would not be hard for a compiler like TypeScript's tsc or some other pre-processor to extract those definitions at compile time to have best of both worlds. But it's likely that they do not do this to remain fully compatible. I am not aware of such tools but would be interested if there are some.

Only user-defined types defined in public modules can be coerced to or from a variant passed to late-bound functions

I have the following code, whereby I have declared an array or records as Member, the Member type consists of 3 entries, forename, surname and distance. I've tried many variations on my 'call' function but I keep getting the same error:
Here is a shortened format of my 'whole' code, hoping someone can point out whatever silly mistake I'm making here, I can only assume something within the parameter passing or declaration of the record structure?
Hope you can help with my school project.
Just like the message says. You're using a private type, and so it can't be coerced to Variant.
You probably want your Read_In_File Sub to instead declare the type of the parameter, rather than using the default Variant type.
Private Sub Read_In_File(ByRef Members As Member())
Though it's very odd to both have a global variable in your class and a parameter of the same name; so I'm not quite sure what exactly you're trying to accomplish.
The error message is telling you exactly how to solve your issue:
"Only user-defined types defined in public modules can be coerced to or from a variant passed to late-bound functions"
Add a Module to your project called MUserDefinedTypes or modUserDefinedTypes (or whatever naming convention you use) and declare your public user-defined type there.
I also agree with Peter in that you have some funny naming conventions in you code. Do not name your variables the same as you modules, classes, forms, or types. It will only get you into trouble. I suggest a simple prefix approach.

Style typing errors in Typescript/React

I am new to TypeScript and am porting a reactjs project. A few errors popped up with the styles, including the tableLayout, borderCollapse and userSelect properties.
Doing some Googling yielded an explanation for this type of error:
https://github.com/Microsoft/TypeScript/issues/18744
https://github.com/Microsoft/TypeScript/issues/11465
and I was able to get rid of the errors with type coercion eg
const someStyle = {
...
userSelect: 'none' as 'none',
...
}
It seems #types/react uses frenic/CSSType. Do these errors represent omissions in their typed styles, or am I doing something wrong? Should they be reported there?
From what I can tell, it's not a result of either omissions nor something you've done. You could even say it's because the types are too specific, but really this is a limitation of typescript itself.
As the first issue mentions, it's an issue with what type is given to an object literal when it's declared (specifically, that object fields holding a string are given the type string and not their literal type).
This isn't something that's likely to ever change, given that there is already a not-too-awful workaround (the type cast you demonstrate). Moreover, from that issue (near the bottom):
we have no plans on changing the design at this point. we already had
multiple iterations on how literal types are inferred and the current
design seems to be the best compromise we can reach.
So ultimately, this is something we have to live with. ¯\_(ツ)_/¯

TCL/C - when is setFromAnyProc() called

I am creating a new TCL_ObjType and so I need to define the 4 functions, setFromAnyProc, updateStringProc, dupIntRepProc and freeIntRepProc. When it comes to test my code, I see something interesting/mystery.
In my testing code, when I do the following:
Tcl_GetString(p_New_Tcl_obj);
updateStringProc() for the new TCL object is called, I can see it in gdb, this is expected.
The weird thing is when I do the following testing code:
Tcl_SetStringObj(p_New_Tcl_obj, p_str, strlen(p_str));
I expect setFromAnyProc() is called, but it is not!
I am confused. Why it is not called?
The setFromAnyProc is not nearly as useful as you might think. It's role is to convert a value[*] from something with a populated bytes field into something with a populated bytes field and a valid internalRep and typePtr. It's called when something wants a generic conversion to a particular format, and is in particular the core of the Tcl_ConvertToType function. You probably won't have used that; Tcl itself certainly doesn't!
This is because it turns out that the point when you want to do the conversion is in a type-specific accessor or manipulator function (examples from Tcl's API include Tcl_GetIntFromObj and Tcl_ListObjAppendElement, which are respectively an accessor for the int type[**] and a manipulator for the list type). At that point, you're in code that has to know the full details of the internals of that specific type, so using a generic conversion is not really all that useful: you can do the conversion directly if necessary (or factor that out to a conversion function).
Tcl_SetStringObj works by throwing away the internal representation of your object (with the freeIntRepProc callback), disposing of the old bytes string representation (through Tcl_InvalidateStringRep, or rather its internal analog) and then installing the new bytes you've supplied.
I find that I can leave the setFromAnyProc field of a Tcl_ObjType set to NULL with no problems.
[*] The Tcl_Obj type is mis-named for historic reasons. It's a value. Tcl_Value was taken for something else that's now obsolete and virtually unused.
[**] Integers are actually represented by a cluster of internal types, depending on the number of bits required. You don't need to know the details if you're just using them, as the accessor functions completely hide the complexity.

Resources