0% found this document useful (0 votes)
10 views87 pages

Tsadvanced

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views87 pages

Tsadvanced

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 87

TYPESCRIPT

A D V A N C E D
THIS BOOK
This book is written (typed) by
Ari, who hails from the South
and has keen interests in
Computer Science, Biology, and
Tamil Literature. Occasionally,
he updates his website, where
you can reach out to him.

https://arihara-sudhan.github.io
Kindly go through the following
book before reading this.book
TYPESCRIPT
TypeScript is described as a
“superset of JavaScript” or
“JavaScript with types.” It is four
things:
>> Programming Language
A language that includes all the
JavaScript syntax, plus new
TypeScript syntax for types.
>> Type Checker
A program that takes in a set of
files written in JavaScript and/or
TypeScript, develops an
understanding of all the
constructs created, and lets the
developer know if it thinks
anything is set up incorrectly.
>> Transpiler
A program that runs the type
checker, reports any issues,
then outputs the equivalent
JavaScript code.
>> Language service
A program that uses the type
checker to tell editors how to
provide helpful utilities to
developers.
EXAMPLES
When we code the following in
TypeScript (I am using
TypeScript playground),

It (The Language Service) will


come to us with a complaint. It
would use its knowledge that
the length property of a string is
a number; not a function. We
can also hover on the highlight
and know what it’s all about.
TypeScript can give us
confidence that changes in one
area of code won’t break other
areas of code that use it.
For an instance, if we change
the number of required
parameters for a function,
TypeScript will let us know if we
forget to update a place that
calls the function.

We can hover on it and know


what happens...
ONCE INFERRED! INFERRED!
Once the type of the data is
inferred, it can’t be changed
later.

It’s not possible to give a data of


another type later.

If we didn’t specify the data, it


will be of any type.
This can be called, Ever
Evolving Variable. Because, it
can get data of different type
over the time.

It is important to note where we


don’t have to use the type hints.
I mean, reduntantly...

In the statement above, it is


redundant to use the type hint.
MODULES
Module is a file with a top-level
export or import. Any file that is
not a module is a Script. A
variable declared in one module
with the same name as a
variable declared in another file
won’t be considered a naming
conflict as long as one file
imports other files variable.

If there is no export or import


statements in a file, it is a script.
Script is where, the variables
declared in one are visible to
others. If the variable name is
already declared in another
script, we can’t declare it in
current script.

If we want to avoid these kind of


stuffs, let’s force our script to be
a module by having an empty
export.
UNIONS
There maybe some cases like
the following.

What could be the type of


nameOfHim when we hover on
it? What could be the type
inferred? It’s Union!

This is an example situation


where we can use Union Type.

Yes.. We use the vertical bar (|)


to denote the union type.
If we have defined a variable
with Union type, the properties
available for that variable are
the common properties of both
the types.

Now, if we hover on it, it will


show like the following.

There will be no errors for


properties which are common to
both the types.
NARROWING
Narrowing is when TypeScript
infers from our code that a value
is of a more specific type than
what it was defined, declared,
or previously inferred as.

TypeScript will understand that


while the variable may later
receive a value of any of the
union typed values, it starts off
as only the type of its initial
value assigned.
TypeScript is smart enough to
understand if we use
conditional constructs while
invoking properties.

We can also do typeof checks.

Why not... We can also go with


truthiness checks i.e., if(name) {...}
LITERAL TYPES
When we define a variable with
const, it’s type will be inferred
as literal.

If we hover on the variable,

Literal is a type itself if used in


the place where type has to be
used.
STRICT NULL CHECKING
In TS, we can enable or disable
type checking. Just look at the
following code where strict null
checking is enabled.

On hovering,

We can also disable it if needed


using the configuring options.
TYPE ALIASES
If we have to use longer union
types that are inconvenient to
type out repeatedly, we can use
type aliases. Actually, it’s not
only for Unions. We can use it to
alias any types.

We can shorten this by using a


type alias as shown below.

Type aliases are a handy feature


to use in TypeScript whenever
our types start getting complex.
For now, that just includes long
union types; later on it will
include array, function, and
object types.
TYPE ALIASES AREN’T JS
Type aliasing feature isn’t that of
JavaScript. It exists in the type
system of TypeScript. Look at
the following.

So, what about something like


Hoisting? Well! There is no such
Hoisting in TypeScript.
When TypeScript compiles our
code, it performs a full pass
over the entire file, gathering all
type information first, including
type aliases, interfaces, and
class types.
This means that even if a type is
declared later in the file, the
compiler already "knows" about
it during this first pass and can
refer to it earlier in the code.
OBJECT TYPES

We do start with the example


shown above. If it is JavaScript,
imagine the situation. We can
access aboutHim.lastName, but
since the lastName property
doesn't exist, it returns
undefined without throwing an
error. (JS lacks static type
checking!) TypeScript enforces
strict type checking.
Since aboutHim doesn't have a
lastName property, trying to
access aboutHim.lastName will
cause a compile-time error like:

We can declare the types of


Object’s Properties as shown
below.
This is one of the places where
we can use type aliasing.
STRUCTURAL TYPING
Structural typing means that two
objects are considered
compatible as long as they have
the same structure, regardless
of what they are explicitly typed
as. TypeScript checks whether
an object has the required
properties for a given type,
instead of strictly checking if it's
an instance of that type.

type WithFirstName and type


WithLastName define two types.
WithFirstName expects an
object with a firstName property
of type string. WithLastName
expects an object with a
lastName property of type
string. hasBoth is an object that
contains both firstName and
lastName properties. When
assigning hasBoth to
withFirstName, TS checks if
hasBoth contains a firstName
property of type string. Since it
does, the assignment is valid.
Similarly, when assigning
hasBoth to withLastName, TS
checks if hasBoth contains a
lastName property of type
string. Again, the assignment is
valid.
TypeScript doesn't care that
hasBoth has extra properties
(lastName in the case of
withFirstName or firstName in
the case of withLastName). As
long as the required structure
(properties and types) matches,
the object can be assigned. This
is structural typing at work: it is
the shape of the object that
matters, not its explicit type.
Types must match with the data!
No compromise here!

And also, we can’t have excess


properties.

Note that excess property


checks only trigger for object
literals being created in
locations that are declared to be
an object type. Providing an
existing object literal bypasses
excess property checks.
NESTED OBJECT TYPES
We can nest objects in JS so for
sure, we can nest types as
shown below:
OPTIONAL PROPERTIES
We can include a ? before the :
in a type property’s type
annotation to indicate that it’s
an optional property.

When we include ? Before the


colon of type annotation, the
property actually takes
undefined union the type
provided. In this case, the type
of year becomes “undefined |
number”. We can either assign a
number or leave it!
OBJECT TYPE UNIONS
Why not? We can also union two
object types.

It means, We can also narrow it


so...
DISCRIMINATED UNIONS
Another popular form of union
typed objects in JavaScript and
TypeScript is to have a property
on the object indicate what
shape the object is. This kind of
type shape is called a
discriminated union, and the
property whose value indicates
the object’s type is a
discriminant.
WE ALSO INTERSECT
What we want an object to
follow multiple type definitions?
We have something called,
intersection. It is an and logic of
types where union is an or logic.

We can also combine


intersection with union...
FUNCTION PARAMETERS
If we need a function to have
parameters of types specified
and to return a value of type
specified, TypeScript says Yes!

We can have optional


parameters as we could have
optional properties.
If we want to have default
values for parameters,
remember! Type inference
happens and once inferred,
inferred!

We can also make it for rest


parameters.

It doesn’t end with these!


Functions are the first class
citizens!
We can pass them to other
functions. So, we need to make
the higher order functions,
equipped with types!

Here, when we declare types


for the function parameters, we
use => to specify their return
type. Beware of the paranthesis!

Function Types can also be


aliased as shown below.
A tip I can give is to declare the
function with type and later
define it as shown below.

When functions don’t want to


return anything, we can declare
their return type as void.
The never type is used to
indicate that a function will
never complete its execution
successfully. This can happen
for a variety of reasons, such as
the function throwing an error,
entering an infinite loop, or
simply not returning a value at
all.

If we want to perform or
simulate something called
Function Overloading in
JavaScript,
We may need to check
arguments.length and proceed
accordingly. But, here in
TypeScript, we can make it
easy!

TypeScript simplifies function


overloading by allowing us to
define multiple function
signatures for the same function
name. This enables us to
specify different parameter
types and return types.
ARRAYS
In JS, what we call as Array can
hold any mixture of values.

TypeScript respects the best


practice of keeping to one data
type per array by remembering
what type of data is initially
inside an array, and only
allowing the array to operate on
that kind of data. I mean, the
type inference...
The type annotation for an array
requires the type of elements in
the array followed by square
brackets [ ].

I hope there is no perplexity!

We have to be careful with the


paranthesis.
Array types can be evolved.
Yes! Every evolving array
members! It is through using
any.

We can define types for


multidimensional arrays.
TypeScript understands typical
index-based access for
retrieving members of an array
to give back an element of that
array’s type.

But, there is actually one caveat.


TS infers the type of the
elements to be what it was
defined as. Can we access an
element using index beyond the
length of the array?
Will it be undefined? No!
Instead, it will be the type
defined.

Arrays can be joined together


using the spread (...) operator. In
TS, if the input arrays are of
same type, the output array will
be that same type. If the input
arrays are of different types,
the output array will be the
union of those types.
TS recognizes and will perform
type checking on the JavaScript
practice of ... spreading an array
as a rest parameter. Arrays used
as arguments for rest
parameters must have the same
array type as the rest parameter.
TUPLES
In JS, what we call as Array can
hold any mixture of values and it
shrinks and expand over the
time. If we want to make it fixed
in terms of size and the order of
types, we can use something
called Tuple.

We should be careful with


inferred arrays. Although they
look similar morphologically,
they will get a union type.
What if we use tuples as rest
parameters?

If we check the variables which


stores the values returned from
a function as an array, the
variables will have a union (of all
return types) type.
If the function is declared as
returning a tuple type and
returns an array literal, that array
literal will be inferred to be a
tuple.

If we want to convert an array


into a read-only tuple, we can
do that with const assertion.

With this, we don’t need to


specify the type explicitly for
creating tuple. But, remember
that, it becomes immutable!
INTERFACES
We can make our own types. It’s
not like aliasing. A type alias is a
way to create a new name for
any type, including primitives,
unions, intersections, and other
types like functions and tuples.
An interface is a way to define
the shape of an object, focusing
primarily on object structure. It
is more flexible when working
with object-oriented concepts
and supports declaration
merging. Interfaces can “merge”
together to be augmented and
can be used to type check the
structure of class declarations
while type aliases cannot.
We’ll see these later. So, let’s
define an interface now.

As in type aliases, we can’t


assign a wrong typed value.
We can also have optional
properties as well.

Besides, we can make a


property read-only using
readonly modifier.
It’s very common in JavaScript
for object members to be
functions. TypeScript therefore
allows declaring interface
members as being the function
types.

Both parameters can be


optional.
A call signature in an interface is
a way to define how a function
should be structured—what
parameters it takes and what
type it returns. Interface defines
a structure or contract for an
object. Meanwhile, A Call
Signature defines how a
function should be called,
including its parameters and
return type.
Index signatures allow us to
define the types of keys and
their corresponding values in an
object, without knowing the
exact names of the keys
upfront.

Remember! It is also possible


with type aliasing.
We cannot mix different types of
values like number with an
index signature like [key: string]:
string. If we want to mix, mix
with same type! Don’t mess with
other types!

Woh! We can also have some


literal types.
We can’t leave the literal
property and assign values
other than defined.

We can nest interfaces within.


TypeScript allows an interface
to extend another interface,
which declares it as copying all
the members of another.

Types can be over-ridden while


extending but remember the
type in base interface must be
compatible with child interface.
TypeScript uses structural
typing, so when we extend
Person, it expects Employee to
still have an age property of
type of string or number.

We can extend multiple


interfaces. This means that an
interface can inherit properties
and methods from more than
one interface.
We combine the structure of
multiple interfaces into one.

Interface merging occurs when


we declare the same interface
multiple times (Subsequent
property declarations ).
TypeScript automatically
merges these declarations into
a single interface, combining
their properties. This is
particularly useful for extending
existing interfaces without
modifying the original
declaration directly.
Note: Subsequent property
declarations must have the
same type!

When we merge two interfaces


that have a method with the
same name but different
parameter types, TypeScript
creates overloads.
CLASSES
Classes are blueprints for
creating objects. They
encapsulate data (properties)
and behavior (methods) into a
single entity, enabling Object-
Oriented Programming
principles.
Class properties are variables
that belong to a class. They
define the state of an object
created from the class.

TypeScript checks that class


properties are properly
initialized before they are used.
If a property is not assigned a
value in the constructor,
TypeScript will raise an error.
Function properties are
methods defined within a class
that can be called on instances
of that class. They define
behaviours associated with the
class.

Although strict initialization


checking is useful most of the
time, we may come across
some cases where a class
property is intentionally able to
be unassigned after the class
constructor.
Definitely Assigned Properties
are properties that are
guaranteed to be initialized
when the class is instantiated.
We can use the ! (definite
assignment assertion) to
indicate that the property will be
initialized later.

You know what... Optional


properties can also exist!
Optional properties are defined
with a ? as we know already.
Read-only properties can only
be assigned a value at the time
of declaration or in the
constructor. After that, their
value cannot be changed.

Classes can be used as types in


TypeScript.
We can create objects that
adhere to the class structure,
allowing for type checking.

Interfaces can describe the


shape of a class, allowing for
the definition of expected
properties and methods. A class
can implement an interface,
enforcing that it adheres to the
interface structure.
Look at the following example.

A class can implement multiple


interfaces, allowing it to adhere
to multiple structures.
A class can extend another
class, inheriting its properties
and methods. This promotes
code reuse and a hierarchical
structure.
When assigning an object of a
derived class to a variable of the
base class type, TypeScript
checks that the derived class is
compatible with the base class.

When a derived class overrides


the constructor of a base class,
it can call the base class
constructor using super().
class Child extends Parent{
constructor( ) {
super( );
}
}
A derived class can override
methods from its base class,
providing a specific
implementation.
class Parent {
method( ) {
console.log(“Parent”);
}
}
class Child extends Parent{
method( ) {
console.log(“Child”);
}
}
When overriding properties in a
derived class, the property must
match the type of the base class
property.
class Parent {
message: string = "Hello";
}
class Child extends Parent{
message: string = "Hi";
}
Abstract classes cannot be
instantiated directly. They can
define abstract methods that
must be implemented by
derived classes.
TypeScript provides access
modifiers for class members:
public, private, and protected.
public: Accessible from
anywhere.
private: Accessible only within
the class
protected: Accessible within the
class and subclasses.
Static members belong to the
class itself rather than to
instances of the class. They are
accessed using the class name.
TYPE MODIFIERS
There are types that can
represent any value. Such types
are called, Top Types. The most
common top type in TypeScript
is any. It can hold values of any
type, allowing maximum
flexibility but at the cost of type
safety.

The following will surely crash


during the run time.
The unknown type is safer than
any. We can assign any value to
it, but before performing
operations on an unknown type,
we need to assert its type or
narrow it down. This provides
better type safety than any.

The keyof operator creates a


union of string literal types
representing the keys of an
object type.
It helps in creating types that
depend on the keys of another
type.

The typeof operator is used to


get the type of a variable or
object at compile time. It can
also be used to create types
based on existing variable
types.
The combination of keyof and
typeof allows us to create a
union of the keys of a variable’s
type.

Type assertions are to tell the


TypeScript compiler to treat a
variable as a specific type,
overriding the type inference.
We can use the as keyword or
angle-bracket syntax.

Assertions change the type of


an existing variable.
Const assertions tell TypeScript
to infer a more specific,
immutable type for a value.

The bottom type refers to the


type that represents no value or
never occurs. This type is
denoted by the keyword never.
GENERICS
Generics are used to define a
component like a function,
class, or interface that can work
with multiple types rather than a
single one. We can think of it as
a way to pass types as
parameters to components. This
increases code flexibility and
reusability.
Look at the following example
which uses Generics.

Here, <T> is a generic type


parameter.
The function identity can take
any type T, and it ensures the
input and return types are the
same. Interested? Let’s write an
example.

We can have multiple type


parameters for more complex
functions.

We can also make an interface


generic.
Look at the following example.

What about making classes


Generic?
Let’s extend a generic class!

It becomes interesting when we


implement a generic interface.
Methods in a class can also be
generic.

We can create generic type


aliases!

We can also apply the readonly,


optional to generics. Just as
default values in methods, we
can also declare the default
type in Generics.
We can constrain generics to
extend a specific type.

keyof with generics lets you


constrain parameters to valid
object keys.

Promises are not exceptions to


TypeScript!
The Promise.reject(err) function,
like the Promise.resolve
method, always returns a
rejected promise. The promise
rejection value is derived from
the error parameter, and its type
is any. The async and await
provide a cleaner, more
readable way to work with
promises in TypeScript. An
async function always returns a
promise, and you use await to
pause the execution until the
promise is resolved or rejected.
Promises can be typed for
better type safety!
NANDRI

You might also like