TypeScript character type? - javascript

My question is very brief. I am new to TypeScript, been searching around here and there but didn't find yet an answer.
Does any experienced TypeScripter know if there is a character Type or an easy way to achieve one?

TypeScript does not have a type for representing fixed-length strings.

I'm not sure about easy, but you could sorta do something with string literal types:
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' | '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' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' // etc....;
of course, this is a fairly brittle solution and breaks down when you consider unicode characters, and I wouldn't really suggest it. As Ryan mentions, JavaScript itself doesn't have any notion of a fixed length string, nor the concept of a char as distinct from a string.

You could use a regex within TypeGuard to contain it's type eg:(you can declare an empty enum to get a new type to be associated with the type guard)
enum CharType { }
export type Char = string & CharType
const isChar = (str: string): str is Char => /^(.|\n)$/.test(
str
)
export function toChar(c: string): Char {
//you can also use is char here for to test whether actually is char
if (!isChar(c)) {
throw new Error('not a char')
}
return c
}
Now Char matches only things that come from calling char(eg actually casted by calling the function rather than just asserted on build time. The compiler simply accepts that a Char is returned and that's actually true if you think of it since it will just throw otherwise)
Original Source(applied to date string):
Atomic Object
Assumptions:
I assume that by mentioning typescript you mean a type used for compile-time checks by typescript compiler and not looking for any kind of optimization on the actual compiled js side(since js only has strings)
The only issue I can see is that you can pass whatever to char function and it will only throw at run time. But you will never reach to a state where you expect a Char and you get something else(since Chars only come from calling char).
On a side note even java casts throw just runtime exceptions.
Although the above approach might not have much to do with casts, I do find some commonalities...

In some comments and answers the lack of a small type of size 8-bit in JavaScript was pointed out. Well, thats not entirely true. With TypedArrays we can create a type of size similar to a classical C-like char. Here a quick take by utilizing Uint8ClampedArray.
type CharString = '!'|'"'|'#'|'$'|'%'|'&'|"'"|'('|')'|'*'|'+'|','|'-'|'.'|'/'|'0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|':'|';'|'<'|'='|'>'|'?'|'#'|'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'|'['|'\\'|']'|'^'|'_'|'`'|'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 CharNumber = 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|96|97|98|99|100|101|102|103|104|105|106|107|108|109|110|111|112|113|114|115|116|117|118|119|120|121|122|123|124|125|126|127|128|129|130|131|132|133|134|135|136|137|138|139|140|141|142|143|144|145|146|147|148|149|150|151|152|153|154|155|156|157|158|159|160|161|162|163|164|165|166|167|168|169|170|171|172|173|174|175|176|177|178|179|180|181|182|183|184|185|186|187|188|189|190|191|192|193|194|195|196|197|198|199|200|201|202|203|204|205|206|207|208|209|210|211|212|213|214|215|216|217|218|219|220|221|222|223|224|225|226|227|228|229|230|231|232|233|234|235|236|237|238|239|240|241|242|243|244|245|246|247|248|249|250|251|252|253|254|255;
class Char extends Uint8ClampedArray {
constructor(char: CharNumber | CharString | Char) {
super(1);
this.setValue(char);
}
get _value(): CharNumber {
return this[0] as CharNumber;
}
set _value(char: CharNumber | CharString | Char) {
if (char instanceof Char) {
this[0] = char._value;
} else if (typeof char === 'number') {
this[0] = char;
} else if (typeof char === 'string') {
this[0] = char.charCodeAt(0);
}
}
setValue(char: CharNumber | CharString | Char) {
this._value = char;
}
toString(): CharString {
return String.fromCharCode(this._value) as CharString;
}
[Symbol.toPrimitive](hint) {
if (hint === 'number') {
return this.toNumber();
}
return this.toString();
}
toNumber(): CharNumber {
return this._value;
}
}
function char(c: CharNumber | CharString | Char) {
return new Char(c);
}
const messageInChars = [
char(72),
char(101),
char(108),
char(108),
char(111),
char(32),
char(83),
char(116),
char(97),
char(99),
char(107),
char(79),
char(118),
char(101),
char(114),
char(102),
char(108),
char(111),
char(119),
];
console.log('char array as string:', messageInChars.join(''));
console.log('calculating with chars:', +messageInChars[0] + +messageInChars[1]);
Thoughts:
In the example, I am using numbers to initialize the Chars. As numbers are doubles, for the short moment of initialization I assume more memory is needed, but not at rest.
Further, I am not entirely sure what extending a TypedArray does to its instance's size.
old answer
A Char is simply a number. Since the basic types of TypeScript do not include Chars what you could do is store a number and convert it back and forth:
var number = "h".charCodeAt(0);
var char = String.fromCharCode(number)
And to something like:
class Char {
private _value:Number;
constructor(char: Number | String){
this.setValue(char);
}
get getValue():String {
return String.fromCharCode(this._value);
}
set setValue(char: Number | String) {
if (typeof char === "number") {
this._value = char;
}
else {
this._value = char.charCodeAt(0);
}
}
}

As I know, the basic types of typescript don't include char.

As a slightly more concise method than listing out all letter characters, you can list out all upper-case or all lower-case characters, then use Lowercase or Uppercase to change their capitalization and unify with the previous type.
type UpperCaseCharacter = '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';
// add additional non-letter characters to this union as desired
type Character = UpperCaseCharacter | Lowercase<UpperCaseCharacter>;

You could just define a wrapper around string, and throw an error if the string is more than one character.
class Character {
readonly char: string;
constructor(char: string) {
if(char.length !== 1) {
throw new Error(char + " is not a single character");
}
this.char = char;
}
toString(): string {
return this.char;
}
}
////////////////////////////////////////
var good: Character = new Character("f");
var bad: Character = new Character("foo"); //error
Of course, you can also add helper methods to the class which operate on the string like toLowerCase(), toUpperCase(), etc.

Related

Angular ngModel doesn't update when `ngModelChange` keeps value

I have a text field represented as: field = {text: "", valid: false}, and an input with [(ngModel)]="field.text".
I want to make that field only accept a defined set of characters (for this issue, numbers), and doing (keypress) doesn't work on mobile, so I did: (ngModelChange)="fieldChanged(field)"
The method does the following:
fieldChanged(field) {
console.log(field.text);
field.text = Array.from(field.text).filter((char:string) => "0123456789".indexOf(char) != -1).join("");
console.log(field.text);
}
And it's behaviour is extremely weird.
Legend:
- input: what key was pressed
- before update: first console.log
- after update: second console.log
- output: what I see on screen in the input
| input | before update | after update | output |
|---------|---------------|--------------|--------|
| "" | "" | "" | "" | <- starting position, no event
| "a" | "a" | "" | "a" |
| "a" | "aa" | "" | "aa" |
| "4" | "aa4" | "4" | "4" |
| "a" | "4a" | "4" | "4a" |
| "a" | "4aa" | "4" | "4aa" |
| "4" | "4aa4" | "44" | "44" |
Why does it always update the output when I enter a legal character? It should be working for each event call.
Edit:
Plunker
I think the cause is that modifying the value on ngModelChange breaks change detection, for example if you change the value back to the previous value, because an invalid character was added.
A workaround:
constructor(private cdRef:ChangeDetectorRef) {}
fieldChanged(field) {
console.log(field.text);
field.text = Array.from(field.text).filter((char:string) => "0123456789".indexOf(char) != -1).join("");
console.log(field.text);
var tmp = field.text;
field.text = null; // or some other value that normally won't ever be in `field.text`
this.cdRef.detectChanges();
field.text = tmp;
this.cdRef.detectChanges(); // I guess this 2nd call won't be necessary
}
If anyone having the issues in updating the value, use setTimeout function while updating
// setTimeout function
setTimeout(() => {
field.text = temp;
this.cdRef.detectChanges();
}, 1);

Remove labels in countdown.js

I'm trying to get countdown.js to return something like this:
Months:Days:Weeks:Hours:Seconds
so something like this with two digits each:
11:02:12:06:12
So far I see the the countdown object has a constructor function which adds the hours and minutes labels, and I can't figure out how to remove them. Here is what I have so far:
var targetDate = new Date(2050, 00, 01);
var totalEnd = targetDate.setHours(targetDate.getHours() + 8);
var time = countdown( null, totalEnd, countdown.HOURS|countdown.MINUTES|countdown.SECONDS);
console.log(time);
Any help would be greatly appreciated. Thanks!
The Countdown.js provides methods
countdown.resetLabels();
countdown.setLabels(singular, plural, last, delim, empty, formatter);
The arguments:
singular is a pipe ('|') delimited ascending list of singular unit
name overrides
plural is a pipe ('|') delimited ascending list of plural unit name
overrides
last is a delimiter before the last unit (default: ' and ')
delim is a delimiter to use between all other units (default: ', ')
empty is a label to use when all units are zero (default: '')
formatter is a function which takes a number and returns a string
(default uses Number.toString()),
allowing customization of the way numbers are formatted, e.g., commas every 3 digits or some unique style that is specific to your locale.
The following would be effectively the same as calling countdown.resetLabels():
countdown.setLabels(
' millisecond| second| minute| hour| day| week| month| year| decade| century| millennium',
' milliseconds| seconds| minutes| hours| days| weeks| months| years| decades| centuries| millennia',
' and ',
', ',
'',
function(n){ return n.toString(); });
Make changes to the above method as per your requirement like (replace "and" & "," with ":")
countdown.setLabels(
' | | | | | | | | | | ',
' | | | | | | | | | | ',
':',
':',
'',
function(n){ return n.toString(); });
countdown(Start Date, End Date, countdown.YEARS | countdown.MONTHS | countdown.DAYS | countdown.HOURS | countdown.MINUTES | countdown.SECONDS , 5).toString();
Reference: http://countdownjs.org/readme.html (search for Localization)
This seemed to work for me:
var targetDate = new Date(2050, 00, 01);
var totalEnd = targetDate.setHours(targetDate.getHours() + 8);
var units = ~(countdown.MILLENNIA |
countdown.CENTURIES |
countdown.DECADES |
countdown.YEARS |
countdown.MINUTES |
countdown.MILLISECONDS);
var time = countdown( null, totalEnd, units);
console.log(time);

What is the purpose of =+ in JavaScript [duplicate]

What does the +d in
function addMonths(d, n, keepTime) {
if (+d) {
mean?
The + operator returns the numeric representation of the object. So in your particular case, it would appear to be predicating the if on whether or not d is a non-zero number.
Reference here. And, as pointed out in comments, here.
Operator + is a unary operator which converts the value to a number. Below is a table with corresponding results of using this operator for different values.
+----------------------------+-----------+
| Value | + (Value) |
+----------------------------+-----------+
| 1 | 1 |
| '-1' | -1 |
| '3.14' | 3.14 |
| '3' | 3 |
| '0xAA' | 170 |
| true | 1 |
| false | 0 |
| null | 0 |
| 'Infinity' | Infinity |
| 'infinity' | NaN |
| '10a' | NaN |
| undefined | NaN |
| ['Apple'] | NaN |
| function(val){ return val }| NaN |
+----------------------------+-----------+
Operator + returns a value for objects which have implemented method valueOf.
let something = {
valueOf: function () {
return 25;
}
};
console.log(+something);
It is a unary "+" operator which yields a numeric expression. It would be the same as d*1, I believe.
As explained in other answers it converts the variable to a number. Specially useful when d can be either a number or a string that evaluates to a number.
Example (using the addMonths function in the question):
addMonths(34,1,true);
addMonths("34",1,true);
then the +d will evaluate to a number in all cases. Thus avoiding the need to check for the type and take different code paths depending on whether d is a number, a function or a string that can be converted to a number.

Regex for validating ratios such as Y39.29/28h

I am trying to write a regular expression which has to adhere the following rules:
Y120.001/100.232k
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
| | | | | | | | This k may be any letter, is not required
| | | | | | | Another number if there was a dot
| | | | | | A dot, not required
| | | | | A number with at least one digit, not required if a letter follows
| | | | Always a slash
| | | If there's a dot, digits can follow
| | A dot, not required
| A number, at least one digit
Always Y
These strings should work:
Y120.001/1k
Y1/h
Y2039/100
Y29/47.0
These should not work:
x203/493s (Not a Y at the start)
Y/39x (No number after the Y)
Y83839 (Second half missing)
Y78/29 (Last letter missing)
This is my early attempt, but it does not work in PHP:
/^\$*(\d)*(.*(\d))\/*(.*(\d))*.$
This pattern should work:
^Y\d+(\.\d*)?/(\d+(\.\d*)?[a-z]?|[a-z])$
Demonstration
In javascript
re=/^Y\d+(\.\d+)?\/([a-z]$|\d+(\.\d+)?[a-z]?$)/
"Y120.001/1k Y1/h Y2039/100 Y29/47.0".split(" ").map(function(s) { return re.test(s) })
> [true, true, true, true]
"x203/493s Y/39x Y83839 Y78/29".split(" ").map(function(s) { return re.test(s) })
> [false, false, false, true]
This does accept Y78/29 as the trailing letter is optional.
After several iterations to correct for commented shortcomings:
/^Y\d+(?:\.\d+)?\/(?:(?:\d+(?:\.\d+)?)[A-Za-z]?|[A-Za-z])$/
Demonstration
Old Answer:
Here is a fully specific version that works well.
/^Y\d+(?:\.\d+)?\/(\d+(?:\.\d+)?)?[A-Za-z]?$/
Edited for a follow-up test in jsfiddle
^Y\d+\.?\d*/((\d+\.?\d*)[a-zA-Z]?|(\d+\.?\d*)?[a-zA-Z])$
You can check the explanation of the regex from here.
If I understand the nuances of your specification
\bY\d+(?:\.\d*)?/(?:[A-Za-z]|(?:\d+(?:\.\d+)?[A-Za-z]?))\b
I understand your specification to include that if the first dot is present, the following number(s) is/are optional; but if the 2nd dot is present, there must be at least one following number. Others seem to have interpreted that part of your requirements differently.
this pattern should work Y[\d.]+\/[\d.]*[a-z]? Demo
What about the following one?
var r = /^Y\d+(\.\d+)?\/(\d+(\.\d+)?|(\d+(\.\d+)?)?[A-Za-z])$/;
console.log(true === r.test('Y120.001/1k')); // true
console.log(true === r.test('Y1/h')); // true
console.log(true === r.test('Y2039/100')); // true
console.log(true === r.test('Y29/47.0')); // true
console.log(false === r.test('x203/493s')); // true
console.log(false === r.test('Y/39x')); // true
console.log(false === r.test('Y83839')); // true
try this:
/Y\d+\.?\d*\/\d+\.?\d*\w?/

Javascript regex matching string ending with number inconsistent

I have the following regex patterns that matches all the 'act' that ends with numbers within a list of URLs.
Regex pattern /\/ch(\d+)\/act(\d+)/gi
Javascript code
pageSrc[1] = "../ch01/index.html";
pageSrc[2] = "../ch01/act01/1.html";
pageSrc[3] = "../ch01/act01/2.html";
pageSrc[4] = "../ch01/act02/1.html";
pageSrc[5] = "../ch01/act02/2.html";
pageSrc[6] = "../ch01/actx/1.html";
var pattern = /\/ch(\d+)\/act(\d+)/gi;
for(var i=0; i<pageSrc.length; ++i){
var hasAct = pattern.test(pageSrc[i]);
console.log(hasAct);
}
The expected results and actual results
| String | Expected Result | Actual Result |
| pageSrc[1] | false | false |
| pageSrc[2] | true | true |
| pageSrc[3] | true | *FALSE |
| pageSrc[4] | true | true |
| pageSrc[5] | true | *FALSE |
| pageSrc[6] | false | false |
Am not sure why pageSrc[3] won't return true. I used the regEx tester on gskinner.com and it worked fine, here is the link http://gskinner.com/RegExr/?344ap
Can anyone help me take a look please? thanks in advance!
Remove the g flag. From the RegExp.test documentation:
As with exec (or in combination with it), test called multiple times on the same global regular expression instance will advance past the previous match.
You don't want a global search when reusing a pattern like this.
> var pageSrc = [];
> pageSrc[1] = "../ch01/index.html";
pageSrc[2] = "../ch01/act01/1.html";
pageSrc[3] = "../ch01/act01/2.html";
pageSrc[4] = "../ch01/act02/1.html";
pageSrc[5] = "../ch01/act02/2.html";
pageSrc[6] = "../ch01/actx/1.html";
var pattern = /\/ch(\d+)\/act(\d+)/i;
for(var i=0; i<pageSrc.length; ++i){
var hasAct = pattern.test(pageSrc[i]);
console.log(i, hasAct);
}
0 false
1 false
2 true
3 true
4 true
5 true
6 false
You are using /g. Remove that flag to make it work.
The g flag makes the regex start matching from pattern.lastIndex (the index where the previous match ended), until it fails, and then start at 0.

Categories

Resources