Understanding TypeScript
Because our company chose Angular as the front-end framework, I started to using TypeScript instead of JavaScript. Now, I completely love TypeScript. It gives us some powerful weapons that JavaScript can’t provide:
- Type System: TypeScript consistently checks variables’ type. This feature significantly lowers the chance of bugs
- OOP: Although JavaScript has
class
since ES6, it still lacks a lot of useful concepts, such asabstract class
,interface
,generic
, etc. These existed in TypeScript. - meta programming: TypeScript provides
decorator
, which can add extra configuration or extra logic to a function or a class. This is widely used in the framework like Angular and NestJS.
Besides, TypeScript offers all of JavaScript’s features. Because all the TypeScript codes finally are compiled in JavaScript.
In short, TypeScript is a superset of JavaScript. JavaScript is still very important for a typescript developer.
I learnt TypeScript from official TypeScript Handbook. This article presents the tools I found interesting in TypeScript.
Type
Type assignment
In TypeScript, there are two ways to assign the type to a variable (similar to Dart):
let a = 5
: the variablea
is assigned asnumber
implicitly.let a: number = 5
: the variablea
is assigned asnumber
explicitly.
I prefer the first way since it ensures the variable has the type I want and it needs less the codes :)
Basic type
As JavaScript, TypeScript has the basic types, such as number
, string
, boolean
, bigint
, symbol
, null
, undefined
. Besides, TypeScript has more.
unknown: It describes the type of variable that we do not know when we are writing an application.
any: It refers to any type. the variable with any
type is the same as the variable in JavaScript.
There is an example to explain the difference between any
and unknown
. But in practice, I wouldn’t assign b
as unknown
because I know b
is a number.
let a: any = 4;
a.toFixed(); // OK, toFixed existslet b: unknown = 4;
b.toFixed(); // Error, Object is of type 'unknown'.
null and undefined: these two are the same as those in JavaScript. However, when using the --strictNullChecks
flag, null
and undefined
are only assignable to unknown
, any
and their respective types .
let a: string;
a = null; // Error, strictNullChecks is active
Union Type
In null and undefined, the string
variable can’t be assigned by null
. This problem can be solved by union type.
let a: string|null;a = 4; // OK
a = null; // OK
This kind of assignment ensures the variable can not be null if we don’t use union type. It avoids many bugs.
object
It is a type that represents the non-primitive type, i.e. anything that is not number
, string
, boolean
, bigint
, symbol
, null
, or undefined
.
To define the types of the properties, we use {}
:
type Text = { id: number, description: string };
Or, we can use an interface
, which will be presented later.
Optional Chain
It is very useful when we visit a property of an object. For example:
a?.b
: the objecta
may have the propertyb
. Ifb
isn’t existed,undefined
is returned.a!.b
: the objecta
must have the propertyb
. Ifb
isn’t existed, an error is thrown.
Function type
Function type contains parameters’ type and return’s type
(firstName: string, lastName?: string) => string
That means a function takes firstName as a required parameter and lastName as an option parameter. Both of them has the string
type. And the function return a value with the string
type.
If the function returns nothing, we use void
.
OOP
class
After ES6, JavaScript has class
. The class
in JavaScript can
- extend another class
- set static attributes and methods
- create
private
orprotected
attributes and methods
TypeScript class can do these, too. Beside, The class
in TypeScript can
- create and extend an abstract class
- create and implement an interface
abstract class
Abstract classes are base classes from which other classes may be derived. They may not be instantiated directly.
Unlike an interface, an abstract class may contain implementation details for its members.
Methods within an abstract class that are marked as abstract do not contain an implementation and must be implemented in derived classes.
For example, to create an abstract class:
abstract class A {
constructor(private _name: string) {} abstract describe(): void get name(): string { return this._name; } set name(newName: string) { this._name = newName; }}
To extend an abstract class:
class B extends A {
constructor(public desc: string) {
super("B");
}
describe() { return this.desc; }}
interface
TypeScript’s interface is very powerful. It defines the types of properties of an object. It is widely used in real-world project. For example, when the front-end receives a json, it will turn the json into an object. We use interface to define the type of the object’s properties.
In the section “object”, I defined Text
type. Now, I use interface to redo it.
interface Text { id: number, description: string }
Both two methods works. But why do we use interface in real-world project ? Because the interface can do more than that.
Interface can have optional properties. For example, we have a text without id. The interface can be created as followed:
interface Text { id?: number, description: string }
It’s very common when we create a new text. Because id
is not existed on server-side when it is just create at front-end.
Interface can have readonly properties. Usually, we don’t want the ID of text changed by the user. The interface can be set:
interface Text { readonly id?: number, description: string }
Interface can add indexable types. When we receive the information like title, author, date, etc. These aren’t presented in Text
interface. We can used indexable types.
interface Text {
readonly id?: number,
description: string,
[propName: string]: any,
}
It adds the flexibility to the interface, but loses the security. It may increase the chance of writing bugs. It’s better to define all the properties.
Interface can be extended by the class. It helps define the attributes’ types and the methods’ types of class.
Interface can be extended by another interface. It’s usually used by generic, which is another powerful tools in TypeScript, presented in the next section.
generic
The components we created should not only have well-defined and consistent APIs, but are also reusable. To make that happen, we need generic.
Generic type can be used in the function. It makes the function reusable.
For example, a function is used by both component A and component B. Component A passes a string to this function, while component B passes a number. Of course, we can use any
or the union type string|number
as the type of the parameter. But, the better practice is using generic:
function fn<T>(arg: T): T { return arg; }// Component A
fn<string>("something");// Component B
fn<number>(1);
Generic keeps both the flexibility and security. It not only makes fn
take any kind of the parameter, but also, ensures the parameter passed in the fn
has the type we want.
Generic type can be used in the class. It makes the class reusable. It’s widely used in creating the components.
class A<T> {
item: T;
getItemName() { return this.item.name; }
}
Now, the component A can take anything as its item. But, the function getItemName
won’t work because TypeScript doesn’t know item
has name
property. To fix this, we need to use interface.
interface Item { name: string; }class A<T extends Item> {
item: T;
getItemName() { return this.item.name; }
}
The generic extends the Item
interface. Now, TypeScript knows item
has name
.
Conclusion
TypeScript improves the security of the codes. It helps the developers make sure what kind of the variable they use. Meanwhile, TypeScript gives the flexibility to developers, too. For example, the interface and the generic let the codes reusable.
The problem is how to balance the security and flexibility. The bad practice may lose the advantage of TypeScript. For example, if we use any
everywhere, we lose the security. If we don’t use any
or generic type, the codes will be hard to reuse.
No tutorial can give us the best methods. Practice makes perfect. Just start to use TypeScript, and find you only way.
Bonus
There is a great tool that help you use TypeScript in a JavaScript project. check it out.