0% found this document useful (0 votes)
12 views

Tsadvanced

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

Tsadvanced

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

TYPESCRIP

T
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