Fundamental concepts to know as a Typescript beginner - Part 1

Fundamental concepts to know as a Typescript beginner - Part 1

This is not a conventional blog on the basics of Typescript but unique things you should know as a beginner.

Play this article

These are the concepts I learned while doing the Frontend Masters TypeScript Fundamentals, v3 course which helped me to understand Typescript better. I hope it will help you too.

This is not a conventional blog on the basics of Typescript but unique things you should know as a beginner. While writing this blog, I assume that you are already familiar with the basics of Typescript. If not, I recommend you go through the introduction article first.

Let's get started!

1. Type Inference

Let's look at a code example and understand what type inference means:

let firstName = "Prerana"

Here, typescript will infer that the type of firstName is a string because we are initializing it with a string value while declaring the variable.


Type inference occurs when we don't explicitly mention the variable type, like in statically typed languages such as C or Java. For example,

  String firstName = "Prerana" // In Java
  let firstName:string = "Prerana" // In Typescript

A variable in TypeScript is born with its type. Now, if we try to change the type of firstName to any other kind, the typescript will give us an error.


Type Inference can occur when initializing variables while assigning function parameter values and function return types and even we are initializing objects with multiple varieties.

2. Literal Types

Let's try to declare a constant value in typescript and examine how it infers its type.

const weekDays = 7

What do you guys think would be the type of weekDays? A number, maybe? 🤔

No, the type of weekDays is 7, which is exactly what's called literal types. In the above example of type inference, firstName can be any string, but here weekDays can only represent one possible number which is 7.


Type 7 is called a literal type.

Let's look at a more helpful example,

const responseCall = (
  data: { name: string; age: number },
  statusText: "success" | " error"
) => {
  console.log(data, status);

responseCall({ name: "Prerana", age: 19 }, "success"); // Successful
responseCall({ name: "Prerana", age: 19 }, "err");  // Error

Here's the type of statusText can only be either a success or an error.


If we try to pass another text, it will fail and give us an error.


3. Type Annotations

Suppose we want to declare a variable but not initialize it simultaneously. Is there any way we can safeguard our variable? How will typescript infer it?

Let's see what happens.

let endDate;

Since typescript doesn't have enough information about the variable, it will infer it with one of the most flexible type: any.


But, if we want to safeguard a variable, we should add a type annotation to it.


The declared data type of a variable cannot be changed by using a different type to change its value. If you try to do so, the typescript compiler will show an error.


Note: It is not mandatory to add type annotations every time since we have type inference in typescript.

4. Type guards

Type guards are used to narrow the variable type and detect runtime failures. They are executed in the runtime and return a boolean value.

In Typescript, a variable can move from a less precise type to a more objective type. This process is called Type Narrowing.

Let's look at an example to understand more:

const multiplication = (a: number, b: number, c?: number): number => {
  if (typeof c !== "undefined") {
    return a * b * c;
  return a * b;

Here, the if conditional block is a type guard. Since the c parameter is optional. We must check whether or not c is undefined and return the value depending on it. There are multiple ways we can use type guard methods:

  1. Literal ways such as === / == / !== / !=

  2. instanceof keyword

  3. typeof keyword

  4. in keyword

  5. Equality narrowing

  6. Custom type guards

5. Excess property checking

A Typescript excess property check ensures an object does not contain any extra properties over those defined in the type annotation.

For example,


Here, we see an error since age is not defined as a parameter which is an excess property. Excess property checking is triggered whenever TypeScript encounters an object literal in an assignment or argument.

However, there are certain limitations to it.

  1. Using type assertions. Typescript won't trigger excess property checking when we assert a type to an object.


  1. Using another variable. Typescript won’t check for excess properties as type Person is a subset of the type inferred for object p1, which is inferred to include all properties in Person plus age. This is known as duck-typing.


6. Index signatures

Objects can be accessed with strings and numbers to contain references to other objects. When only the key and value types are known, typescript uses index signatures to type unknown objects.

Let's see it with an example:


In place of the property name, we place the type of the key inside the square brackets: { [key: keyType]: valueType }.

Note: The key of the index signature can only be a string, number, or symbol. Other types are not allowed and will throw an error.


7. Discriminated Unions

Discriminated unions are when we have two different types with a similar literal type which can be used to distinguish between them.

Here's an example:


Please do not mistake this with a string type that has two different values; it is a literal type. The state is a literal type. Since the first case is a success, the typescript automatically suggests only the error value in the else block.

As a result, TypeScript will do a type narrowing considering that the object must be of the type with that specific literal. Discriminated unions are also called Tagged Unions.

8. Open Interfaces

In Typescript, you can have multiple Interfaces declarations in the same scope, which is impossible with type aliases. The Typescript compiler allows reopening interface declarations, i.e., merging several declarations of the same interface into a single declaration. It is useful when adding new fields to an existing interface. This is seen more often when you are using some library types or global window objects.

interface Error {
  exampleProperty: string;

const getError: Error = {
  name: "Type Error",
  exampleProperty: "example",


name and message are the original properties that are non-optional on the global Error object. The interface Error includes all the fields originally declared, plus the new field exampleProperty that we declared individually by us. Both declarations have been merged.

9. Recursive type

Recursive types are auto-referential types with infinite nesting possibilities. The use of infinitely nestable arrays of numbers is one example. In other words, it is circularly referenced.

type NestedNumbers = number | NestedNumbers[];

let nums: NestedNumbers = [3, 4, [5, 6, [7], 59], 221];

It will produce an error if we try to push a string type.


10. what is .d.ts?

d stands for Declaration Files that only contain type information. These files don't produce javascript output. They are automatically generated at build time, separating type information and javascript code.

For example,

// serviceCall.ts
export const responseCall = (
  data: { name: string; age: number },
  status: "success" | " error"
)  => {
  return 'some code'

responseCall({ name: "Prerana", age: 19 }, "success");
// serviceCall.d.ts
export declare const responseCall: (data: {
    name: string;
    age: number;
}, status: "success" | " error") => string;

It's time to wrap things up for today. Thank you very much for your patience.

There's so much to learn, but it's all achievable if you don't give up.

Let me know one new concept you learned from this blog that you weren't aware of. I would love to know your answer and feedback :)

Did you find this article valuable?

Support Prerana Nawar by becoming a sponsor. Any amount is appreciated!