I am using typescript and want to call the chalk method dynamically, see my code :
import chalk from 'chalk';
const color: string = "red";
const message: string = "My Title";
const light: boolean = false;
const colorName = `bg${capitalize(color)}${light ? 'Bright' : ''}`;
console.log(chalk[colorName](message));
So, the color function takes as a value the color of the background of the message. The message is the content and the light is the boolean used to know if the background should have its bright version used.
The problem is that typescript throws this error :
Element implicitly has an 'any' type because expression of type 'any' can't be used to index type 'ChalkInstance'.
on the chalk[colorName] part.
I already tried to put (chalk[colorName] as keyof ChalkInstance). And already looked at this post (which is the basically the same problem). Obviously, the solutions answered aren't working with my case.
There are a few things you need to do.
Remove the redundant explicit types from your variables color, message, and light. Doing so is not only unnecessary, it actually interferes with TypeScript properly inferring the literal value of those types. In other words, if you define const color = "red"; TypeScript will know, because it is a const, that it will never change and it can always be treated as the string literal "red" instead of the more generic string. (As a general rule, you should never explicitly define the type of a const variable.)
const color = "red";
const message = "My Title";
const light = false;
Make sure your capitalize() function properly defines its return type. In this case, there is actually an awesome built-in utility type Capitalize<> which you can use here. In combination with a TypeScript generic, you can define the function in such a way that TypeScript knows that e.g. if "red" is what goes in, "Red" is what comes out.
function capitalize<S extends string>(c: S) {
return c.replace(/\b\w/g, firstLetter => firstLetter.toUpperCase()) as Capitalize<S>;
}
Use the as const assertion when you define colorName (and any other similar variables you may need to define). If you don't do this, the type of colorName will be inferred as string, which is no good for indexing chalk. With as const, you are basically telling TypeScript to treat the resulting expression as a literal string value. In this case, the type of colorName becomes "bgRedBright" | "bgRed", both of which are valid indices of chalk.
const colorName = `bg${capitalize(color)}${light ? 'Bright' : ''}` as const;
^^^^^^^^
When you put it all together:
import chalk from 'chalk';
const color = "red";
const message = "My Title";
const light = false;
function capitalize<S extends string>(c: S) {
return c.replace(/\b\w/g, firstLetter => firstLetter.toUpperCase()) as Capitalize<S>;
}
const colorName = `bg${capitalize(color)}${light ? 'Bright' : ''}` as const;
console.log(chalk[colorName](message));
Edit: It's possible in the future that you would need to define your color variable in such a way that it is dynamic and not just a hard coded literal "red" value. In that case you need a way to satisfy TypeScript that color will always be something that is valid given all the other inferences that we just set up.
Here, we actually do want to explicitly define a type for certain variables, particularly let and function arguments. Fortunately, chalk provides a very useful type for this case, ForegroundColor which is essentially all of the valid "base" colors and happen to be compatible with BackgroundColor when put into the form `bg${ForegroundColor`, which is exactly what we need here.
import chalk, { ForegroundColor } from 'chalk';
let color: ForegroundColor = "red";
color = "blue";
We could even improve our capitalize() function by more strictly controlling what the type of the argument can be:
function capitalize<S extends ForegroundColor>(c: S) {
return c.replace(/\b\w/g, firstLetter => firstLetter.toUpperCase()) as Capitalize<S>;
}
Another playground example that puts those further improvements into practice.
I recommend Colors.ts for TypeScript
npm install colorts
import 'colorts/lib/string';
console.log('hello'.green); // outputs green text
console.log('i like cake and pies'.underline.red) // outputs red underlined text
console.log('inverse the color'.inverse); // inverses the color
https://github.com/shaselle/colors.ts
I found the answer to my question myself. The problem was that the colorName variable was of type any and chalk couldn't recognize it as an index of the chalk class. Here the value of the colorName variable could contain all the value of the backgroun colors whether they were bright or not.
I then had two solutions :
Hard code all the values of the colors (which would take a long time)
Find the type representing the colors and explicit set as the type of the colorName variable
My solution is close from the second idea, however, the Chalk library doesn't provide a type for the colors. However, we can import the explicit list of all the BackgroundColors. Then, we only have to asign the type of these keywors as the type of the colorName variable.
import { BackgroundColor } from 'chalk';
import chalk from 'chalk';
const color: string = "red";
const message: string = "My Title";
const light: boolean = false;
const colorName = `bg${capitalize(color)}${light ? 'Bright' : ''}` as typeof BackgroundColor;
console.log(chalk[colorName](message));
PS: Note that the type of the variabkes color, message and light are hard-coded. On the application, these values are dynamic, that's a way to make it more clear.
Related
How do I convert the data type if I know the Variant.Type from typeof()?
for example:
var a=5;
var b=6.9;
type_cast(b,typeof(a)); # this makes b an int type value
How do I convert the data type if I know the Variant.Type from typeof()?
You can't. GDScript does not have generics/type templates, so beyond simple type inference, there is no way to specify a type without knowing the type.
Thus, any workaround to cast the value to a type only known at runtime would have to be declared to return Variant, because there is no way to specify the type.
Furthermore, to store the result on a variable, how do you declare the variable if you don't know the type?
Let us have a look at variable declarations. If you do not specify a type, you get a Variant.
For example in this code, a is a Variant that happens to have an int value:
var a = 5
In this other example a is an int:
var a:int = 5
This is also an int:
var a := 5
In this case the variable is typed according to what you are using to initialized, that is the type is inferred.
You may think you can use that like this:
var a = 5
var b := a
Well, no. That is an error. "The variable type can't be inferred". As far as Godot is concerned a does not have a type in this example.
I'm storing data in a json file: { variable:[ typeof(variable), variable_value ] } I added typeof() because for example I store an int but when I reassign it from the file it gets converted to float (one of many other examples)
It is true that JSON is not good at storing Godot types. Which is why many authors do not recommend using JSON to save state.
Now, be aware that we can't get a variable with the right type as explained above. Instead we should try to get a Variant of the right type.
If you cannot change the serialization format, then you are going to need one big match statement. Something like this:
match type:
TYPE_NIL:
return null
TYPE_BOOL:
return bool(value)
TYPE_INT:
return int(value)
TYPE_REAL:
return float(value)
TYPE_STRING:
return str(value)
Those are not all the types that a Variant can hold, but I think it would do for JSON.
Now, if you can change the serialization format, then I will suggest to use str2var and var2str.
For example:
var2str(Vector2(1, 10))
Will return a String value "Vector2( 1, 10 )". And if you do:
str2var("Vector2( 1, 10 )")
You get a Variant with a Vector2 with 1 for the x, and 10 for the y.
This way you can always store Strings, in a human readable format, that Godot can parse. And if you want to do that for whole objects, or you want to put them in a JSON structure, that is up to you.
By the way, you might also be interested in ResourceFormatSaver and ResourceFormatLoader.
I'm working on a template literal type that'd only match title cased strings. I thought I'd first create a single word matcher using Uppercase<>:
const uppercaseWord: Uppercase<string> = 'U';
but it seems to match lowercase letters too:
const uppercaseWord: Uppercase<string> = 'u';
this one won't throw.
I've tried parametrizing it:
type SingleUpperCase<Str extends string> = `${Uppercase<Str>}`;
const upperCaseWord: SingleUpperCase = 'U';
But the type argument could not be inferred. It works by passing a string literal explicitly:
type SingleUpperCase<Str extends string> = `${Uppercase<Str>}`;
const upperCaseWord: SingleUpperCase<'u'> = 'U';
it works, but I'd need something more generic. Something that matches any uppercase string. If I tried again passing string as the type argument, the error is resolved, but the lowercase letter can be assigned without an error:
type SingleUpperCase<Str extends string> = `${Uppercase<Str>}`;
const upperCaseWord: SingleUpperCase<string> = 'u'; // no error
I wonder if this is even possible given it'd require a regex-like string matching from the compiler.
Aside: I'm not exactly sure what rules you have for "title case"; I don't know if "HTML String" is in title case or not. For what follows, I will assume that you just need to make sure that the first character of the string and the first character after every space (" ") is not lowercase. That means "HTML String" is fine. If you have a different rule, you can adjust the code in the answer below.
There is no specific type in TypeScript that represents title-cased strings. Template literal types don't give this to you; while Uppercase<string> and Capitalize<string> are now their own types as of TypeScript 4.8, implemented in microsoft/TypeScript#47050, they won't enforce handle the spacing requirements.
For a true title-case string type you would need, as you said, something like regular expression validated string types. There is an open issue at microsoft/TypeScript#41160 asking for use cases for such regex-validated types; if the solution below doesn't meet your needs, you might want to comment on that issue with your use case, why it is compelling, and why the alternative solutions don't suffice.
While there is no specific type that works here, you can write a recursive template literal type TitleCase<T> which can be used as a constraint on T. Meaning that T extends TitleCase<T> if and only if T is a title-cased string.
Then, in order to save people from having to annotate their strings with some generic type, you'd write a helper function like asTitleCase() which just returns its input, but produces a compiler error if you pass in a bad string.
So, while your ideal solution here would look like this:
/* THIS IS NOT POSSIBLE
const okay: TitleCase = "This Is Fine"; // okay
const error: TitleCase = "This is not fine"; // error
const alsoError: TitleCase = String(Math.random()); // error
*/
the implementable solution looks like this:
const okay = asTitleCase("This Is Fine"); // no error
const error = asTitleCase("This is not fine"); // error!
// ---------------------> ~~~~~~~~~~~~~~~~~~
// Argument of type '"This is not fine"' is not assignable to
// parameter of type '"This Is Not Fine"'.
const alsoError = asTitleCase(String(Math.random())); // error!
// Argument of type 'string' is not assignable to parameter of type
// '"Please Use a Title Cased String Literal Here, Thx"'
Again, this is what is implementable, not what is ideal. All uses of title-cased string types will need to gain an extra generic type parameter.
Note that you probably don't need to actually write asTitleCase(...) unless you want to see the error right at the declaration. Presumably you have some function (say, lookupBookTitle()) that cares about title case. If so, you'd just make that function generic and enforce the constraint there. So instead of const str = asTitleCase("XXX"); lookupBookTitle(str);, you'd just write const str = "XXX"; lookupBookTitle(str); The only difference is where the error shows up.
Also, inside the implementation of something like lookupBookTitle(), you should probably just widen the input to string and just treat it as if it's already been validated. Even though T extends TitleCase<T> has the effect of enforcing the constraint on callers, the compiler won't be able to follow the logic when T is an unspecified generic type parameter:
// callers see a function that constrains title to TitleCase
function lookupBookTitle<T extends string>(title: VerifyTitleCase<T>): Book;
// implementer just uses string
function lookupBookTitle(title: string) {
const book = db.lookupByTitle(title);
if (!book) throw new Error("NO BOOK");
return book;
}
Anyway, here's the implementation:
type TitleCase<T extends string, D extends string = " "> =
string extends T ? never :
T extends `${infer F}${D}${infer R}` ?
`${Capitalize<F>}${D}${TitleCase<R, D>}` : Capitalize<T>;
The type TitleCase<T, D> splits the string T at the delimiter D, and capitalizes (first character is uppercased) each piece. So it turns a string into a title-cased version of itself:
type X = TitleCase<"the quick brown fox jumps over the lazy dog.">
// type X = "The Quick Brown Fox Jumps Over The Lazy Dog."
Then we can write a VerifyTitleCase<T> type that checks if T extends TitleCase<T>. If so, it resolves to T. If not, it resolves either to TitleCase<T>, or some hard-coded error string that hopefully gives users an idea what went wrong. (There are no "throw types" or "Invalid types" in TypeScript, as requested in microsoft/TypeScript#23689; so using a hard-coded error string literal is a workaround):
type VerifyTitleCase<T extends string> = T extends TitleCase<T> ? T :
TitleCase<T> extends never ? "Please Use a Title Cased String Literal Here, Thx" :
TitleCase<T>
And finally, the helper function:
const asTitleCase = <T extends string>(s: VerifyTitleCase<T>) => s;
Playground link to code
The TypeScript team has been doing great work on string literal typing in recent updates (4.1 & 4.2). I am wondering if there is a way to type a fixed length string.
Ex.
type LambdaServicePrefix = 'my-application-service';
type LambdaFunctionIdentifier = 'dark-matter-upgrader';
type LambdaFunctionName = `${LambdaServicePrefix}-${LambdaFunctionIdentifier}`; // error: longer than 32 characters...
How I imagine it would go is something like, Array<64, string>;. TypeScript has the Tuple type so as an array I could fix length of an array. [string, ... string * 62, string].
type FutureLambdaIdType = `${LambdaServicePrefix}-${string[32]}`;
UPDATED to reflect better recursive conditional type support
There are still, as of TS 4.7, no regular-expression-validated string types in TypeScript. Template literal types handle some, but not all, of the use cases for such regex types. If you have a situation like this where template literal types are insufficient, you might want to go to microsoft/TypeScript#41160 and describe your use case. The idea of a "string whose maximum length is N characters" for some N extends number would be easy enough to express with regex types, but is not easily achievable with template literals.
Still, let's see how close we can get.
A major roadblock stands in the way. TypeScript cannot easily represent the set of all strings less than N characters as a specific type StringsOfLengthUpTo<N>. Conceptually any given StringsOfLengthUpTo<N> is a large union, but since the compiler balks at unions with more than ~10,000 members, you can only describe strings of up to a few characters this way. Assuming you want to support the 95 characters of 7-bit printable ASCII, you will be able to represent StringsOfLengthUpTo<0>, StringsOfLengthUpTo<1>, and even StringsOfLengthUpTo<2>. But StringsOfLengthUpTo<3> would exceed the compiler's capacity, since it would be a union of over 800,000 members. So we have to give up on specific types.
Instead we can think of our type as a constraint used with generics. We need a type like TruncateTo<T, N> which takes a type T extends string and an N extends number and returns T truncated to N characters. Then we can constrain T extends TruncateTo<T, N> and the compiler would automatically warn on too-long strings.
It used to be that shallow recursion limits would prevent us from writing TruncateTo<T, N> for N greater than about 20 or so, but TypeScript 4.5 introduced support for tail recursion elimination on conditional types. That means we can write TruncateTo<T, N> by adding some extra accumulator arguments like this:
type TruncateTo<T extends string, N extends number,
L extends any[] = [], A extends string = ""> =
N extends L['length'] ? A :
T extends `${infer F}${infer R}` ? (
TruncateTo<R, N, [0, ...L], `${A}${F}`>
) :
A
This works by having an A accumulator to store the string we're building up, and an L arraylike accumulator that keeps track of how long that A string is (string literal types don't have a strongly typed length property, see ms/TS#34692 for the relevant request). We build up A one character at a time until we either run out of the original string, or until we reach a length of N. Let's see it in action:
type Fifteen = TruncateTo<"12345678901234567890", 15>;
// type Fifteen = "123456789012345"
type TwentyFive = TruncateTo<"123456789012345678901234567", 25>;
// type TwentyFive = "1234567890123456789012345"
We can't directly write T extends TruncateTo<T, N> as TypeScript complains that this is a circular constraint. But we can at least write a helper function like this:
const atMostN = <T extends string, N extends number>(
num: N, str: T extends TruncateTo<T, N> ? T : TruncateTo<T, N>
) => str;
and then you could call atMostN(32, "someStringLiteral") and it would either succeed or warn based on the the length of the string literal argument. Note that the str input is of a weird conditional type, whose sole purpose is to avoid the circular constraint. T is inferred from str, and then checked against TruncateTo<T, N>. If it succeeds, great. Otherwise, we give str the type of TruncateTo<T, N>, and we'll see an error message. It works like this:
const okay = atMostN(32, "ThisStringIs28CharactersLong"); // okay
type Okay = typeof okay; // "ThisStringIs28CharactersLong"
const bad = atMostN(32, "ThisStringHasALengthOf34Characters"); // error!
// -------------------> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// '"ThisStringHasALengthOf34Characters"' is not assignable to parameter of type
// '"ThisStringHasALengthOf34Characte"'.
type Bad = typeof bad; // "ThisStringHasALengthOf34Characte"
Is it worth it? Maybe. The original answer here had to do some unsavory things to get even a fixed-length check. The current one isn't so bad, but it's still a bunch of effort to get a compile-time check. So you might still have a use case for regex-validated string types.
Playground link to code
There is no way to represent fixed-length strings with Typescript. There is a very upvoted proposal here, but still this feature has not been released.
If the length is very little, there are some workarounds suchs as the following:
type Char = 'a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'i'|'j'|'k'|'l'|'m'|'n'|'o'|'p'|'q'|'r'|'s'|'t'|'u'|'v'|'w'|'x'|'y'|'z'
type String3 = `${Char}${Char}${Char}`
const a: String3 = 'aa' // error
const b: String3 = 'bbbbb' // error
const c: String3 = 'ccc' // OK
const d: String3 = 'abc' // OK
But you can't handle big lengths since you will run into a "Expression produces a union type that is too complex to represent" error.
It is impossible to limit the length of string by typing or typescript utils.
You can, however, use regex to validate the string (including length):
/^([a-zA-Z0-9_-]){1,64}$/
type IsThirteen<T extends number> = 13 extends T ? true : never
type IsFifteen<T extends number> = 15 extends T ? true : never
type LengthOfString<S extends string, T extends string[] = []> = S extends `${string}${infer R}`
? LengthOfString<R, [...T, string]>
: T['length'];
type IsLengthThirteenOrFifteen<T extends string> = true extends IsThirteen<LengthOfString<T>>
? T
: true extends IsFifteen<LengthOfString<T>>
? T
: never
function IsLengthThirteenOrFifteenGuard <T extends string>(a: IsLengthThirteenOrFifteen<T>) {
return a;
}
const b = IsLengthThirteenOrFifteenGuard('1131111111111')
Sources:
StringOfLength
Playground
I have a dictionary with Structs in it. I am trying to assign the values of the struct when I loop through the dictionary. Swift is telling me cannot assign to 'isRunning' in 'blockStatus'. I haven't been able to find anything in the docs on this particular immutability of dictionaries or structs. Straight from the playground:
import Cocoa
struct BlockStatus{
var isRunning = false
var timeGapForNextRun = UInt32(0)
var currentInterval = UInt32(0)
}
var statuses = ["block1":BlockStatus(),"block2":BlockStatus()]
for (block, blockStatus) in statuses{
blockStatus.isRunning = true
}
cannot assign to 'isRunning' in 'blockStatus'
blockStatus.isRunning = true
This does work if I change the struct to a class.
I am guessing it has something to do with the fact that structs are copied and classes are always referenced?
EDIT: So even if it is copying it.. Why can't I change it? It would net me the wrong result but you can change members of constants just not the constant themselves. For example you can do this:
class A {
var b = 5
}
let a = A()
a.b = 6
Your guess is true.
By accessing blockStatus, you are creating a copy of it, in this case, it's a constant copy (iterators are always constant).
This is similar to the following:
var numbers = [1, 2, 3]
for i in numbers {
i = 10 //cannot assign here
}
References:
Control Flow
In the example above, index is a constant whose value is automatically set at the start of each iteration of the loop.
Classes and Structures
A value type is a type that is copied when it is assigned to a variable or constant, or when it is passed to a function. [...] All structures and enumerations are value types in Swift
Methods
Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.
However, if you need to modify the properties of your structure or enumeration within a particular method, you can opt in to mutating behavior for that method. The method can then mutate (that is, change) its properties from within the method, and any changes that it makes are written back to the original structure when the method ends. The method can also assign a completely new instance to its implicit self property, and this new instance will replace the existing one when the method ends.
You can opt in to this behavior by placing the mutating keyword before the func keyword for that method:
You could loop through the array with an index
for index in 0..<statuses.count {
// Use your array - statuses[index]
}
that should work without getting "cannot assign"
If 'Y' in this case is a protocol, subclass your protocol to class. I had a protocol:
protocol PlayerMediatorElementProtocol {
var playerMediator:PlayerMediator { get }
}
and tried to set playerMediator from within my player mediator:
element.playerMediator = self
Which turned into the error cannot asign 'playerMediator' in 'element'
Changing my protocol to inherit from class fixed this issue:
protocol PlayerMediatorElementProtocol : class {
var playerMediator:PlayerMediator { get }
}
Why should it inherit from class?
The reason it should inherit from class is because the compiler doesn't know what kind your protocol is inherited by. Structs could also inherit this protocol and you can't assign to a property of a constant struct.
Preface: I'm working with Processing and I've never used Java.
I have this Processing function, designed to find and return the most common color among the pixels of the current image that I'm working on. the last line complains that "The method color(int) in the type PApplet is not applicable for the arguments (String)." What's up?
color getModeColor() {
HashMap colors = new HashMap();
loadPixels();
for (int i=0; i < pixels.length; i++) {
if (colors.containsKey(hex(pixels[i]))) {
colors.put(hex(pixels[i]), (Integer)colors.get(hex(pixels[i])) + 1);
} else {
colors.put(hex(pixels[i]),1);
}
}
String highColor;
int highColorCount = 0;
Iterator i = colors.entrySet().iterator();
while (i.hasNext()) {
Map.Entry me = (Map.Entry)i.next();
if ((Integer)me.getValue() > highColorCount) {
highColorCount = (Integer)me.getValue();
highColor = (String)me.getKey();
}
}
return color((highColor);
}
The Processing docs that I'm looking at are pretty sparse on the HashMap so I'm not really sure what's going on inside it, but I've been augmenting what's available there with Java docs they point to. But I'm not really grokking what's happening with the types. It looks like the key in the HashMap needs to be a string and the value needs to be an integer, but they come out as objects that I have to cast before using. So I'm not sure whether that's causing this glitch.
Or maybe there's just a problem with color() but the docs say that it'll take a hex value which is what I was trying to use as the key in the HashMap (where I'd rather just use the color itself).
Now that I've talked through this, I'm thinking that the color() function sees the hex value as an int but the hex() function converts a color to a string. And I don't seem to be able to convert that string to an int. I guess I could parse the substrings and reconstruct the color, but there must be some more elegant way to do this that I'm missing. Should I just create a key-value-pair class that'll hold a color and a count and use an arraylist of those?
Thanks in advance for any help or suggestions you can provide!
I'll dig deeper into this, but an initial thought is to employ Java generics so that the compiler will complain about type issues (and you won't get runtime errors):
HashMap<String,Integer> colors = new HashMap<String,Integer>();
So the compiler will know that keys are Strings and elements are Integers. Thus, no casting will be necessary.
I didn't figure it out, but I did work around it. I'm just making my own string from the color components like:
colors.put(red(pixels[i]) + "," + green(pixels[i]) + "," + blue(pixels[i]),1)
and then letting the function drop a color out like this:
String[] colorConstituents = split(highColor, ",");
return color(int(colorConstituents[0]), int(colorConstituents[1]), int(colorConstituents[2]));
This doesn't really seem like the best way to handle it -- if I'm messing with this long-term I guess I'll change it to use an arraylist of objects that hold the color and count, but this works for now.