TypeScript —
Learn the basics
https://unsplash.com/photos/Xz7MMD5tZwA
Photo by https://unsplash.com/photos/Xz7MMD5tZwA
Types? Typing? What is it?
This article is part of the collection “TypeScript Essentials”, this is the Chapter three.

 

 

If you come from chapter one and two, you’re now motivated to start using TypeScript, so, let’s get started! 🚀

 

 

Types? Typing? What is it?

In simple words, types (in web languages):
  • helps the compilers to 
    • optimise the storage of the values in memory — at runtime
    • optimise code (mainly for transpilers, ex: Dart) — at compile time
  • helps developers to know how to use a value (methods, structure, etc)

 

JavaScript — The dynamic weakly typed

A JavaScript variable can contain any type of data, 
JavaScript is dynamically typed, this means that the type of a variable is defined by the type of its data, at runtime.
1let a = 1; // a is a number  
2a = 'ok'; // a is now a string
Though, since it’s weakly typed, you can do “what ever you want” in your expression.
This means that you don’t need to inform the compiler how to convert your values, example :
1let a = 1;  
2let b = "2";  
3let c = a + b; // "12" -> c is a string  
4c = a \* b; // 2 -> c is a number

 

The ECMA specification describe how implementations should behave based on values types during an operation (additive, etc): 
https://tc39.github.io/ecma262/#sec-additive-operators
This might look much simpler or cool than a * (int) b
however this weak typing can introduce some weird type conversion.
1const sum = (a, b) => a + b;  
2sum(1, "2") // "12" -> ??!?!?!
That’s why sometimes you need to “force value type” to avoid runtime unexpected behaviours by using parseInt() or the + (unary operator) .
1const sum = (a, b) => parseInt(a, 10) + parseInt(b, 10);  
2sum(1, "2") // 3
3
4// Or   
5const sum = (a, b) => (+a) + (+b);

 

Then, how to evaluate a variable type in JavaScript ?
  • by using typeof:
typeof is a built-in operator that returns the type of a value by looking in the “type flag” (see MDN article)
Though, beware of typeof null bug ⚠️
  • by using “duck typing” —
With normal typing, suitability is assumed to be determined by an object’s type only. In duck typing, an object’s suitability is determined by the presence of certain methods and properties (with appropriate meaning), rather than the actual type of the object.
➡️ In few words, we “guess” the type looking at the value structure, like lodash do with isArrayLike() , isDate() , … helpers:  https://github.com/lodash/lodash/blob/master/isArrayLike.js#L26-L28

 

TypeScript —Static Structural typing (with erasure)

TypeScript, as a layer over JavaScript,  add an optional static typing system.
As we’re going to see later, TypeScript bring a lot of more complete type system but some theoretical things must be covered.

 

Structural Typing
Like “duck typing”, TypeScript will look the shape/structure of a type, instead of the type itself.
This is different from C# or Java, and it bring the same flexibility as JavaScript.
1type Person = { name: string; };  
2type Animal = { name: string; kind: string; };  
3const isPerson(p: Person): p is Person => !!p.name;
4
5let a: Animal = { name: 'flocon', kind: 'cat' };  
6isPerson(a) // true is TypeScript, impossible in C#/Java
7
More details in the official TypeScript FAQ.

 

Type erasure
Type erasure means that all types and annotations are removed at transpile time.
Since JavaScript is still dynamically typed, leaving some comments about types in the code would be pointless for V8 or other engine that have their owns optimisation systems.
However, if you look for Reflection, you should definitely read Chapter 6 : Make types “real”, the type guard functions.

 

 

Write our first types with scalars

The term “scalar” comes from linear algebra, where it is used to differentiate a single number from a vector or matrix. The meaning in computing is similar. It distinguishes a single value like an integer or float from a data structure like an array.⠀

 

Use types in TypeScript
It’s really simple, just use a : suffix to any variable, class property declaration or interface property as below:
1const myVar**: string** = 'hello';
2
3type myObjectType = { a**: string**; b**: number**; }
4
5class MyClass {  
6   myProp**: string**;  
7   /\* ... \*/  
8}
Please notice the space after each `:` , it’s important to avoid confusion with object property declaration.

 

The Scalar types
TypeScript define the same scalar types as JavaScript, except it adds a enum , any and never ones:
  • never
The never type represents the type of values that never occur. 
Specifically, never is the return type for functions that never return and never is the type of variables under type guards that are never true.
🚨Beware, this is incorrect: let a: never = undefined; never shows the absence of type, you can see it as the opposite of any .
  • any
The variable can contains any type of data, like in JavaScript by default.
1let a: any = “hello world”; // valid  
2let a: any = 1; // valid  
3let a: any = undefined; // valid
  • string
1let a: string = “hello world”
  • array (not a scalar though.)
1let a: string[] = [“a”, “b”, “c”] 
2
3// Or tuple :
4let a: [string, number] = [1, 2]
  • object (not a scalar though.)
1let a: object = {} // correct
2
3let a: object = { a: 1 }   
4// not correct, we'll see this in next section with "interface"
  • boolean
1let a: boolean = true; // correct
  • enum
1enum Color {Red, Green, Blue}   
2let c: Color = Color.Green; // (value in JS is a int)
  • null
1let a: null = null; // correct  
2let a: null = undefined; // incorrect, it should have a null value.
  • undefined
1let a: undefined;  
2let a?: string; // undefined | string
⚠️ Notice: By default, all types includes null and undefined , as says the documentation:
The type checker previously considered null and undefined assignable to anything. Effectively, null and undefined were valid values of every type and it wasn’t possible to specifically exclude them (and therefore not possible to detect erroneous use of them).
--strictNullChecks switches to a new strict null checking mode.
In strict null checking mode, the null and undefined values are not in the domain of every type and are only assignable to themselves and any
So, let a: string = undefined;  is correct without the _--strictNullChecks_ compiler option.
🚨 Beware : do not use String or Object types, those are primitive class types.

 

Rich types: interface and type

The interface keyword

The scalar types are essential but not sufficient for “real world” usage.
We need to describe complex data structure as types like objects, classes, etc
TypeScript helps us to describe theses types in a “structural way” — as seen in the first paragraph — with the interface keyword.
Interface is used to describe objects, functions or classes.

 

Describe a User object
1interface User {  
2   name: string;  
3   birthday?: string; // optional property  
4}
As seen in the previous paragraph, the birthday property type is string | undefined .
Since in JavaScript a object property with undefined value is considered missing, having a nullable type property mean a optional object property.

 

Describe a Function
1interface MyAddFunction {  
2   (a: number, b: number): number  
3}
4
5const add: MyAddFunction = (a, b) => a + b;
Here, the root () describe a callable object, in another way, a function.
We’ll see in the next paragraph how to type a function.

 

Describe an Cat type by re-using Animal type.
Since interfaces are named, you can extends and “re-open” them.
Example:
1interface Animal {  
2  name: string;  
3}
4
5interface Cat extends Animal {  
6  mustache\_len: number;  
7}
8
9// ---------------------
10
11interface A { a: string; }  
12interface A { b: string; }
13
14// is the same as 
15
16interface A {  
17   a: string;  
18   b: string;  
19}

 

The type keyword

The type keyword is used to create “type aliases”, in other words :
  • renaming a existing type:
1type MyString = string;
Useful for documentation purposes.
  • compose existing types : union types, augmenting, …
1type PossibleValues = "open" | "close";  
2const a: PossibleValues = "open";
3
4interface Car {  
5   speed: string;  
6}
7
8type CompetitionCar = Car & { competitor\_id: string };
We will see more useful example is future articles about advanced types.

 

What the difference between interface and type?

Given the following, example, we could say that interface and type are the same:
1interface A {  
2   a: string;  
3}
4
5type B = { a: string; }
But as explains the official doc and the stack-overflow answer in source, there is some fundamental differences:
  • interface can be edited, they are open; type are closed.
  • type can compose existing types with union, pick etc
To conclude, we can say that:
  • type is especially useful to build intermediate type composed of others
  • interface is useful for build fundamental and re-usable types

 

Function types, arguments, return type

Declared types, interfaces and variables are finally used 
to declare function signatures.
A function signature (or type signature, or method signature) defines input and output of functions or methods.
A signature can include:
- parameters and their types
- a return value and type
- exceptions that might be thrown or passed back
- information about the availability of the method in an object-oriented program (such as the keywords public, static, or prototype).
https://developer.mozilla.org/en-US/docs/Glossary/Signature/Function

 

Let’s type some functions

1// inline typing  
2const capitalize = (str: **string**): **string** => {  
3   return str.charAt(0).toUpperCase() + str.slice(1);  
4}
5
6// types with interfaces  
7interface CapitalizeFunction {  
8   (str: string) => string;  
9}
10
11const capitalize: **CapitalizeFunction** = str => {  
12   return str.charAt(0).toUpperCase() + str.slice(1);  
13}
14
15// function as property in object  
16interface **ReactComponentProps** {  
17   onClick: (e: React.MouseEvent<HTMLDivElement>) => void;  
18}
19
20class MyComponent extends React.Component<**ReactComponentProps**\> { }
As you can see, a function type is always using () , only return type change depending on the context:
  • : for “inline return type — to avoid conflict with arrow function definition
  • => for “interface function type”
ℹ️ Please note that “interface function type” is not usable for function declared function — only for “anonymous functions” aka “arrow functions”.

 

Object as arguments

Let’s consider this example
1interface MyFunctionArgs {  
2   name: string;  
3   security\_code: string;  
4   id?: number;  
5}
6
7function isCompleteUser({ name, id = 1 }: MyFunctionArgs): boolean {  
8   return !!name;  
9}
10
11isCompleteUser({ name: 'a', security\_code: 'b' });
Here, arguments destructuring is clear, the function only pick name property.
This notation is particularly useful for compact function signature and optional and named arguments.
I personally find this syntax useful for “data validation” functions.

 

 

Conclusion

In this chapter we saw:
  • That TypeScript bring clearer variable typing with static typing.
  • How the TypeScript typing system behave in a structural and static way.
  • That TypeScript bring all fundamental types and even more with nifty ones like never or any .

 

 

Again, thanks to Sylvain PONTOREAU for the review and inspiration.
We use cookies to collect statistics through Google Analytics.
Do not track
 
Allow cookies