Blog-Archiv

Samstag, 30. Juni 2018

How to Make TypeScript Check JavaScript

Experienced programmers know that projects written in languages that do not support typing get uncontrollable at a certain size, although this is widely ignored. Now we have this problem with user-interface development in JavaScript, no it's more, it's the whole presentation layer that changed from the server to the web-browser side. A presentation layer is a big thing. And of course, with all our legacy applications, there is no time to rewrite thousands of JavaScript source lines to TypeScript.

But can we make the TS compiler check our JS sources? Would it find type incompatibilities and other mistakes? The TS handbook promises it, so let's verify.

Compiler Switches

The tsc compiler provides an abundance of switches. To make it check the example checkme.js source we must use three of them (I refer here to tsc 2.7.2):

tsc --checkJs --allowJs --noEmit   checkme.js
  1. --checkJs: check also .js sources for correctness
  2. --allowJs: allows the file extension .js, the compiler insists on that when having --checkJs
  3. --noEmit: do not generate JS source, without this switch you would receive the error message
    error TS5055: Cannot write file 'checkme.js' because it would overwrite input file.
    

Open a commandline terminal window, change to the directory where your JS sources reside, and launch the commandline above. The tsc compiler will now check them.

Plain JS Sources

Let's try to compile following JS source code that obviously contains typing mistakes:

let count = 123;
count = "Hello";

let myName = "Garfield";
myName = 321;

const countList = [];
countList.push("World");
countList.push(1);

You should not assign a string value to the variable count that previously contained a number. The same for string-variable myName that receives a number. Then we have an array that we fill with both a number and a string, which is a mistake normally.

Put that code into a checkme.js file and compile it with tsc --checkJs --allowJs --noEmit checkme.js. In the following I have documented the resulting error-messages in the source lines that cause them:

let count = 123;
count = "Hello";  // error TS2322: Type '"Hello"' is not assignable to type 'number'.

let myName = "Garfield";
myName = 321;  // error TS2322: Type '321' is not assignable to type 'string'.

const countList = [];
countList.push("World");
countList.push(1);  // not an error!

The compiler detected the re-typing of the variables, but did not report the inconsistent array contents. For function calls we would get no results at all. So this is quite a poor check, but TS provides a better solution.

JS Sources with JsDoc Typing

They named it "type annotations" because the type-hints are prefixed by an ampersand "@", nevertheless these hints are inside JsDoc /** comments */. It looks like the following:

/** @type {number} */
let count = 0;

/**
 * @param {string} message - required, the message to display.
 * @param {string} [prefix] - optional, the prefix for the message.
 * @return {number} the number of characters in given message.
 */
function sayHello(message, prefix) {
    console.log(prefix+": "+message);
    return message.length;
}

These JsDoc annotations fully type the variable and the function, and all abuses will be reported by the TS compiler!

Here comes a buggy implementation, together with the resulting compiler messages:

/** @type {number} */
const count = "Hello";  // error TS2322: Type '"Hello"' is not assignable to type 'number'.

/**
 * @param {string} message - required, the message to display.
 * @param {string} [prefix] - optional, the prefix for the message.
 * @return {number} the number of characters in given message.
 */
function sayHello(message, prefix) {
    prefix = prefix || 1234;  // error TS2322: Type 'string | 1234' is not assignable to type 'string'.
    console.log(prefix+": "+message);
    return message;  // error TS2322: Type 'string' is not assignable to type 'number'.
}

/** @type {string} */
const result = sayHello(1, 2);  // error TS2345: Argument of type '1' is not assignable to parameter of type 'string'.
                                // error TS2322: Type 'number' is not assignable to type 'string'.

The variable count is typed to be a number by a JsDoc type annotation. When it receives a string value, the compiler detects and reports that pitfall. Inside function sayHello(), the string parameter prefix receives a default value that is a number, which is a mistake. The function returns a string, but should return a number. Finally the function is called with two number parameters, but both should be string. Its return is assigned to a string variable, but the return is number.

Put this into some .js file and compile it with tsc --checkJs --allowJs --noEmit *.js to see these error messages. The exit code of the compiler is 1 (non-zero), thus you can detect the problem in an automated build process.

Here comes array typing with JsDoc, and the resulting error message. The countList is typed to be a number-array, but receives a string, which is reported to be an error.

/** @type {number[]} */
const countList = [];
countList.push("World");  // error TS2345: Argument of type '"World"' is not assignable to parameter of type 'number'.

Not bad, isn't it?

Conclusion

You can also type classes and object literals. Find more information in the JsDoc-Support and Type-Checking Wiki articles on the TypeScript web page. Another quite useful TS page is the FAQ.




Freitag, 22. Juni 2018

CSS Variables

When no one expects it any more, it happens: CSS got variables! They were called "Custom Properties", reason may be that their definitions look nearly the same as property definitions. The value of a such a custom property can then be received by a "CSS Variable".

Definition

A CSS custom property definition is written like a property definition, just that the property name is preceded by a "--":

body {
    --border: 0.6em solid gray;
}

This custom property is defined in the global context of the HTML <body> element, thus it should be available everywhere. Its base name can be any name, even the name of an existing CSS property like "border". Mind that these names are case-sensitive. The body selector can be replaced by any possible CSS selector.

Would the custom property have been defined on <div>, then it would not be known in <body>, a custom property is bound to the context of its rule. Its value can change when a deeper element overwrites it, but going back upwards would restore the former value ("cascading variables"). The strength of a variable is bound to the specificity of its selector.

Usage

After we defined a custom property, we would like to use it as variable value:

article {
    border: var(--border);
}

Here it was used as value for the actual CSS border property. The var() function provides the value of the custom property to the CSS border property. It doesn't insert the value, it just provides it. (I'll come back to this later.)

Mind that you can use variables just on the right side of a rule. Following attempt to generate the left-side property name margin-top from a variable will fail:

.foo {
    --side: margin-top;
    var(--side): 20px;
}

Also building together values doesn't work:

.foo {
    --gap: 20;
    margin-top: var(--gap)px;
}

This is an attempt to declare the pixel unit on the actual rule only, but it will fail.

On the other hand this will work:

input::before {
    --label: 'This is my text';
    --highlighted-label: var(--label)' highlighted';

    content: var(--highlighted-label);
}

Here two texts are built together to "This is my text highlighted".

Asset?

The actual asset of a variable is avoiding copy & paste programming.

/* variable definition */
body {
    --border: 0.6em solid gray;
}
/* variable usage */
header {
    border: var(--border);
}
nav {
    border: var(--border);
}
aside {
    border: var(--border);
}
footer {
    border: var(--border);
}

Here all rule-sets take advantage of the same variable. Changing the border in all these elements requires the change of just one line of code.

But is this really an asset? Look at this:

header, nav, aside, footer {
    border: 0.6em solid gray;
}

Does the same in just one line of code!

Let's look deeper into it, and decide later whether "Custom Property" is a useful feature.

Calculations

By means of the CSS calc() function we can calculate values also with variables:

body {
    --padding: 0.6em;
}
div {
    padding: calc(var(--padding) - 0.4em);
}

The variable --padding is used to calculate a concrete padding for any <div> element. The result will be 0.2em.

Asset!

A variable gets updated any time its custom property changes, and all calculations done using variables also will be updated! This is something that no CSS preprocessor like Sass or Less provides, they just substitute text.

section    {
    --big-font: 4em;

    font-size: var(--big-font);
    /* font-size is 4em */

    --big-font: 3em;
    /* now font-size has become 3em */
}

Redefining the custom property "--big-font" would immediately change the font-size CSS property. A CSS variable is a reference to a custom property, not a copy of its value.

This I would regard to be a real asset: reactive programming has reached CSS!

Variable Cascading

Borders are not inherited to sub-elements in CSS, thus they are well suited to demonstrate the cascading ability of variables. (With colors this would be not so obvious, because colors are cascaded to sub-elements by default.)

 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
<!DOCTYPE HTML>
<html>
    <head>
        <style>
            body {
                --border: 0.6em solid blue;
            }
            main {
                --border: 0.6em solid green;
            }
            p {
                --border: 0.6em solid red;
            }
            * {
                border: var(--border);
            }
        </style>
    </head>
    
    <body>
        The BODY element has a blue border.
        
        <div>
            A blue border inherited from BODY would be around this DIV.
        </div>
        
        <main>
            This MAIN would have a green border.
            <p>
                Here around this P would be a red border.
            </p>

            <div>
                In this DIV, red has been reset to green from MAIN parent.
            </div>

        </main>
        
        <div>
            Green has been reset to blue in this DIV.
        </div>

    </body>

</html>

In the header's <style> element, the <body> element defines a custom property "--border" declaring a blue border, <main> redefining it to a green border, <p> to a red border. Mind that for <div> no custom property was defined.
Finally all elements (the * selector) are given a CSS border property that refers to the "--border" custom property (which has been defined three times).

The subsequent HTML then uses <div>, <main> and <p> in different combinations. It would look like the following:

The BODY element has a blue border.
A blue border inherited from BODY would be around this DIV.
This MAIN would have a green border.

Here around this P would be a red border.

In this DIV, red has been reset to green from MAIN parent.
Green has been reset to blue in this DIV.

As expected the <body> gets a blue border. The <div> sub-element of it has a blue border again, because every element was given a border, and the setting is cascaded from <body>. The nested <main> has a green border, because there is a CSS selector matching it, and the contained rule redefines the "--border" variable. Same with the further nested <p> element, which has a red border. After leaving the <p> the color jumps back to green, as we see on the <div> inside <main>, and after leaving <main> back to blue. Thus the last <div> element has a blue border again.

Fallbacks

The var() function provides place for a default value:

div {
    --thick-border: 0.6em solid gray;
}
p {
    --thin-border: 0.3em solid lightGray;
}
article {
    border: var(--thick-border, var(--thin-border));
}
section {
    border: var(--thick-border, 1px solid black);
}

The <article> rule evaluates to the variable "--thick-border", and, in case that does not exist, uses "--thin-border" as default. This would happen in case the <article> element was not wrapped into a <div> but into a <p>, because the "--thick-border" variable appears just in the context of a <div> element. In case it was wrapped in neither, it would have no border.

The <section> rule defines the concrete setting "1px solid black" as default for the case it was not wrapped into a <div> element.

Conclusion

Intuitively, as a reader and programmer, I do not like the -- prefix. It is too close to --i; which is for decrementing the numeric value of variable i in many C-like languages.

But, all in all, custom properties are worth being tried out. The auto-update feature makes it a great tool for reactive user interfaces.




Mittwoch, 13. Juni 2018

TypeScript Function Types

TypeScript (TS) interfaces can describe function signatures. The thing implementing such an interface must be callable like a function, so it must be a function. A "callable interface", hard to imagine for an OO-thinking Java developer, actually exists in TS:

interface SearchFunction
{
    (text: string, pattern: string): boolean;
}

const search: SearchFunction = function(text: string, pattern: string): boolean {
    return text.search(pattern) >= 0;
}

const contained = search("ABCDEF", "EF");
console.assert(contained === true, "Pattern 'EF' is expected to be found in 'ABCDEF'");

The constant search is typed to the interface SearchFunction, and assigned to be a function that has the signature described in the interface. The subsequent test shows that this actually works.

The distinction between function and object (interface) is becoming quite diffuse here. Most likely this is another TS feature just to integrate already existing JavaScript APIs. From the TS language specification:

An object type containing one or more call signatures is said to be a function type.
Function types may be written using function type literals or by including call signatures in object type literals.

The TS Handbook speaks about "Hybrid Types". A TS interface can describe a mix of function and object, and if you also include index signatures it would be a mix of function, object and array!

Terms

Following terms might be worth to learn:

Call Signature
(text: string, pattern: string): boolean
Function Type
interface SearchFunction
{
    (text: string, pattern: string): boolean;
}
Function Type Literal
(text: string, pattern: string) => boolean

Yes, it's not a typo, right of the arrow is the return type!
Using a function type literal, we could type above search() function, without interface, like this:

const search: 
    (text: string, pattern: string) => boolean = 
    function(text: string, pattern: string): boolean {
        return text.search(pattern) >= 0;
    }

Or even like that:

const search: 
    (text: string, pattern: string) => boolean = 
    (text: string, pattern: string): boolean => {
        return text.search(pattern) >= 0;
    }

Baffled? I think freedom of expression in context of computer languages undermines human common sense. Murphy's Law stands by: what can be misunderstood will be misunderstood! Just count the minutes you need to understand this code.

Conclusion

Here is a mix of object, function, and array, all in one interface.
The <JackOfAllTrades> in line 13 is a cast-operator (mind that there are two kinds of casts in TS, the other one would be an as JackOfAllTrades in line 15).

 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
interface JackOfAllTrades
{
    (): string;  // function signature
    
    [shotInTheFoot: string]: any;  // index signature
    
    index: number;  // object-property
    
    contains(text: string, pattern: string): boolean;  // object-method
}

function newJackOfAllTrades(): JackOfAllTrades {
    const jack = <JackOfAllTrades> function(): string {
        return "Jack";
    };
    
    jack["Hello"] = "World";
    
    jack.index = 1;
    
    jack.contains = function(text: string, pattern: string): boolean {
        return text.search(pattern) >= 0;
    };
    
    return jack;
}

const jack = newJackOfAllTrades();

console.assert(jack() ===  "Jack", "jack() is expected to return 'Jack'");
console.assert(jack["Hello"] === "World", "value at 'Hello' is expected to be 'World'");
console.assert(jack.index === 1, "index is expected to be 1");
console.assert(jack.contains("ABC", "B"), "'ABC' is expected to contain 'B'");

This is not only syntactically correct, but also all assertions are successful! Whether it is useful for everyday programming may be doubted.




Sonntag, 10. Juni 2018

TypeScript Constructor in Interface

In TypeScript you can describe a constructor in an interface, using the new keyword:

interface Point
{
    new(x: number, y: number);

    x: number;
    y: number;
}

(Marked red because it is not possible that way, although syntactically correct!)

The TypeScript language specification says about this

An object type containing one or more construct signatures is said to be a constructor type.

and

Constructor types may be written using constructor type literals or by including construct signatures in object type literals.

Lots of new terms, further explanations missing. Constructors can not be described in Java interfaces, such a TypeScript feature would be something completely new.

Considering this, a question comes to my mind: What if you receive a JSON object from an AJAX call, cast it to an interface that holds a constructor signature, could you then construct a new object of that type, maybe even without having loaded the definition of that class? I can't believe this. But when no, what sense does it make to give a constructor signature in an interface?

On stackoverflow I found this:

Construct signatures in interfaces are not implementable in classes; they're only for defining existing JS APIs that define a 'new'-able function.

So it looks like you can't use them in interfaces to describe the constructor of the implementing class. This syntax exists just to describe JavaScript built-in types like Date or RegExp.

New Terms

The construct signature

new <RETURNTYPE>(...constructorParameters: any): RETURNTYPE

would look like the following in an example for the type Point

new (x: number, y: number): Point

The constructor type literal

new <RETURNTYPE>(...constructorParameters: any) => RETURNTYPE

is equivalent to the object type literal

{
    new <RETURNTYPE>(...constructorParameters: any): RETURNTYPE;
}

Thus following example describing the constructor of type Point

new (x: number, y: number) => Point

is equivalent to

{
    new (x: number, y: number): Point;
}

Test Code

Compiling the following TypeScript code

ConstructableInterface.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
interface Point
{
    new(x: number, y: number);
    
    x: number;
    y: number;
}

class PointImpl implements Point
{
    readonly x: number;
    readonly y: number;
    
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
}

const point: Point = new PointImpl(1, 2);

you would receive the compile error

ConstructableInterface.ts(9,7): error TS2420: Class 'PointImpl' incorrectly implements interface 'Point'.
  Type 'PointImpl' provides no match for the signature 'new (x: number, y: number): any'.
ConstructableInterface.ts(20,7): error TS2322: Type 'PointImpl' is not assignable to type 'Point'.
  Type 'PointImpl' provides no match for the signature 'new (x: number, y: number): any'.

Mind that the compiler reports any as return-type of the construct signature, so it not even infers that the result would be the type itself! The TS Handbook justification is:

This is because when a class implements an interface, only the instance side of the class is checked. Since the constructor sits in the static side, it is not included in this check.

To use a construct signature in an interface you must implement it like the following:

FactoryFunction.ts
 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
interface Point
{
    x: number;
    y: number;
}

interface PointConstructor
{
    new(x: number, y: number): Point;
}

class PointImpl implements Point
{
    readonly x: number;
    readonly y: number;
    
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
}

function newPoint(pointConstructor: PointConstructor, x: number, y: number): Point {
    return new pointConstructor(x, y);
}

const point: Point = newPoint(PointImpl, 1, 2);

You must separate the constructor description from the type description (interface) and put it into its own interface. The class PointImpl must not to implement PointConstructor, this is implicitly checked by the compiler when calling newPoint(). In case you implement it, you will see this compile error:

FactoryFunction.ts(12,7): error TS2420: Class 'PointImpl' incorrectly implements interface 'PointConstructor'.
  Type 'PointImpl' provides no match for the signature 'new (x: number, y: number): Point'.

The interface PointConstructor seems to be the constructor type. Finally, in the line const point: Point = newPoint(PointImpl, 1, 2), the constructor turns out to be the name of the implementing class, a little bit unexpected.

Conclusion

Currently I can not find any practical sense in this, because instead of passing the type to the factory I could also construct an object of that type directly via new. But maybe I haven't had to integrate JS implementations into TS code yet.




Freitag, 1. Juni 2018

Static Factory and Generics in TypeScript

In my last Blog I showed how a static factory could make sense when needing overrides already at construction time. In this Blog I want to improve that, trying to avoid repetitive code in the static factories:

FirstName.ts
    public static newFirstName(): FirstName {
        const firstName: FirstName = new FirstName();
        firstName.init();
        return firstName;
    }
LastName.ts
    public static newLastName(): LastName {
        const lastName: LastName = new LastName();
        lastName.init();
        return lastName;
    }

For this I need to look into TypeScript (TS) generics.

Generics Example

Swapping two array elements is a classic example where a function needs to be reusable for different data-types. We want to sort arrays that contain strings as well as such that contain numbers or objects. Following shows how to write such a generic function in TS, including two example calls.

function swap<T>(array: T[], firstIndex: number, secondIndex: number): void {
    const newSecond: T = array[firstIndex];
    array[firstIndex] = array[secondIndex];
    array[secondIndex] = newSecond;
}

const stringArray: string[] = [ "One", "Two", "Three" ];
swap(stringArray, 0, 2);

const numberArray: number[] = [ 1, 2, 3 ];
swap(numberArray, 0, 2);

The generic data-type is denoted the by the <T> after the swap function name. You could use any letter or name. Convention is to keep generics upper-case, to indicate their meta-data role.

It is the same syntax as Java generics have. Like in Java, also <T extends SomeClass> is possible inside the angle brackets, but not <T super SomeClass> (which would restrict the possible types to super-classes of SomeClass).

Following example

    init<T extends Name>(instance: T): T {
        // ....
        return instance;
    }

reads as:

init() uses a type T that must be at least Name, but can be also a sub-class of Name.

The incoming parameter must be of that type, and the returned object will be of that type. You can use T also for local variables.

So let's try to use generics for improving our static factories.

Generic Initialization

We must keep the constructors of all sub-classes of the common super-class Name protected to avoid factory circumventions. Thus we still need to have a static factory in each sub-class, because just the class itself has access to its protected constructor, but that factory will be just one line of code.

Here is the new super-class with its generic static init() function:

Name.ts
 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
export abstract class Name
{
    /** Static generic factory. */
    public static init<T extends Name>(instance: T): T {
        instance.init();
        return instance;
    }
    
    private value: string;

    /** Protect construction to allow factory only. */
    protected constructor(name?: string) {
        if (name !== undefined)
            this.value = name;
    }
    
    /** Object initialization, to be called by factory. */
    protected init(): void {
        if (this.value === undefined)
            this.value = this.getDefault();
    }

    /** Sub-classes must define a default. */
    protected abstract getDefault(): string;
    
    /** Expose the value readonly. */
    public getValue(): string {
        return this.value;
    }
}

The generic static init() function declares the data-type it handles, and calls init() on the instance it receives (see line 5 and 18), then it returns the instance (see line 6).

There is something new in abstract class Name: an optional name can be passed to the constructor, making the example a little more realistic. Because of that, the initialization of the default can take place just when the value is still empty after construction.

Refactored Factories

Now here comes the one line of code in the refactored sub-class:

FirstName.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import { Name } from "./Name.js";

export class FirstName extends Name
{
    public static newInstance(name?: string): FirstName {
        return Name.init(new FirstName(name));
    }
    
    public readonly defaultValue: string = "(No Firstname)";

    protected constructor(name?: string) {
        super(name);
    }
    
    protected getDefault(): string {
        return this.defaultValue;
    }
}

The static factory newInstance() needs to define exactly the same parameters as the constructor does. It creates a new instance, passing it to the super-class Name.init(), and returns whatever that returns (see line 6). The TS compiler guesses T from the type of the parameter, the caller doesn't need to declare it explicitly. This is called "type inference".

Here is the second sub-class:

LastName.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import { Name } from "./Name.js";

export class LastName extends Name
{
    public static newInstance(name?: string): LastName {
        return Name.init(new LastName(name));
    }
    
    public readonly defaultValue: string = "(No Lastname)";

    protected constructor(name?: string) {
        super(name);
    }
    
    protected getDefault(): string {
        return this.defaultValue;
    }
}

That's the way to keep all kinds of initialization inside the static init() of the super-class.
Here is test code for it:

import { FirstName } from "./FirstName.js";
import { LastName } from "./LastName.js";


const firstName: FirstName = FirstName.newInstance();
console.assert(
    firstName.getValue() === firstName.defaultValue,
    "firstName.getValue() is expected to be '"+firstName.defaultValue+"': '"+firstName.getValue()+"'");

const PETER: string = "Peter";
const firstName2: FirstName = FirstName.newInstance(PETER);
console.assert(
    firstName2.getValue() === PETER,
    "firstName2.getValue() is expected to be '"+PETER+"': '"+firstName2.getValue()+"'");


const lastName: LastName = LastName.newInstance();
assert(
    lastName.getValue() === lastName.defaultValue,
    "lastName.getValue() is expected to be '"+lastName.defaultValue+"': '"+lastName.getValue()+"'");

const LEWIS: string = "Lewis";
const lastName2: LastName = LastName.newInstance(LEWIS);
assert(
    lastName2.getValue() === LEWIS,
    "lastName2.getValue() is expected to be '"+LEWIS+"': '"+lastName2.getValue()+"'");

This shows:

firstName.getValue() is expected to be '(No Firstname)': '(No Firstname)'
firstName2.getValue() is expected to be 'Peter': 'Peter'
lastName.getValue() is expected to be '(No Lastname)': '(No Lastname)'
lastName2.getValue() is expected to be 'Lewis': 'Lewis'

Conclusion

Of course things get more complicated in reality. All logic normally done in constructor needs to move to the static factory of a specific class. Being there it is not overridable and reusable any more, because static implementations are no more object-oriented. When having complex initialization logic it will be recommendable to write a dedicated builder and pass an instance of that alternatively to the static factory.