Getting started
- Introduction
- Fundamentals
- Build in Types
- Advanced Types
What is typescript ?
#### Created from microsoft to address some of the shortcomings of JavaScript, TypeScript is there to make your JS robust
TypeScript is a superset of JavaScript that adds static typing to the language. Unlike JavaScript, where variables can hold any type of value and types are inferred at runtime, TypeScript allows you to specify types explicitly during development.
These types are checked by the TypeScript compiler before your code is transpiled into regular JavaScript, helping catch type-related errors early in the development process.
Why do we need TypeScript
- Static Typing and Type Safety: TypeScript’s most significant advantage is static typing. By declaring types for variables, function parameters, and return values, you can catch type-related bugs before they reach the runtime environment. This enhances code quality, reduces runtime errors, and improves maintainability.
- Enhanced Tooling: TypeScript-aware IDEs and code editors provide improved code suggestions, auto-completion, and better code navigation due to the availability of type information. This makes development more efficient and helps developers understand APIs and code structures more easily.
- Readability and Maintainability: Explicitly declared types make the code more self-documenting, making it easier for other developers (or even your future self) to understand the intended data structures and flows. This is especially beneficial in larger codebases.
- Refactoring and Codebase Evolution: TypeScript aids in refactoring code by allowing you to safely change types without manually searching and updating each occurrence. This becomes crucial as projects evolve over time.
- Interface and Contract Enforcement: TypeScript’s type system allows you to define interfaces and contracts for APIs, ensuring that functions and objects adhere to specific structures. This helps prevent potential bugs when integrating various parts of an application.
- Early Error Detection: Type errors are caught at compile time, which means you can catch mistakes before running your code. This can save a lot of time during development and testing phases.
- Migration from JavaScript: If you’re already using JavaScript, adopting TypeScript can be done gradually. TypeScript is a superset of JavaScript, meaning that valid JavaScript code is also valid TypeScript code. You can start adding types to your existing JavaScript codebase and gradually transition to using more advanced TypeScript features.
- Popular Frameworks and Libraries: TypeScript is widely used in conjunction with popular frontend frameworks like Angular, React, and Vue.js. These frameworks provide TypeScript typings, enhancing the development experience and ensuring that you’re using their APIs correctly.
- Large-Scale Applications: TypeScript is particularly useful for large-scale applications where code organization, maintainability, and collaboration among developers become more challenging. The type system provides more control over data flow and interactions.
Differences Between TypeScript and Vanilla JavaScript:
- [/] Type System:
- ➡️ TypeScript enforces static types, meaning you declare the type of variables, function parameters, and return values. In vanilla JavaScript, types are determined at runtime.
- [/] Type Annotations:
- ➡️ TypeScript requires type annotations (e.g.,
number
,string
,boolean
) to indicate the type of variables, parameters, and functions. Vanilla JavaScript doesn’t have this requirement.
- ➡️ TypeScript requires type annotations (e.g.,
- [/] Compile Step:
- ➡️ TypeScript code needs to be transpiled into JavaScript using the TypeScript compiler. Vanilla JavaScript is executed directly by the browser or JavaScript engine.
- [/] Tooling and IDE Support:
- ➡️ TypeScript-aware tools and IDEs provide richer features, such as intelligent code completion and type-related suggestions, which are not as robust in vanilla JavaScript.
- [/] Early Error Detection:
- ➡️ TypeScript detects errors at compile time, while vanilla JavaScript may only reveal errors during runtime, potentially leading to more debugging time.
- [/] Code Complexity Management:
- ➡️ TypeScript’s type system aids in managing the complexity of large-scale applications, reducing potential issues arising from data interactions.
- [/] Compatibility with ES6 and Beyond:
- ➡️ TypeScript supports features from modern JavaScript versions (ES6 and beyond), making it possible to use the latest language features even when targeting older browsers.
Drawbacks of TS
While TypeScript offers numerous benefits, there are also some drawbacks and challenges associated with using the language. It's important to be aware of these aspects when considering whether TypeScript is the right choice for a particular project:
- Learning Curve: Transitioning from JavaScript to TypeScript requires learning new concepts related to type annotations, interfaces, and other TypeScript-specific features. This initial learning curve can slow down development at the beginning.
- Verbose Syntax: The requirement to explicitly declare types and annotate variables and functions can lead to more verbose code compared to dynamically typed languages like JavaScript.
- Development Speed: While TypeScript’s type checking helps catch errors early, it can also slow down the development process due to the time spent on type annotations and potential issues arising from stricter type enforcement.
- Build Process: TypeScript code needs to be transpiled to JavaScript before it can run in a browser or on a Node.js server. This extra build step can add complexity to the development workflow.
- Compatibility with Libraries: While many popular libraries and frameworks have TypeScript typings available, not all third-party libraries and plugins may have well-maintained type definitions. This can lead to issues when integrating external code.
- Type Complexity: In complex applications, managing complex type hierarchies and interactions can become challenging. Balancing the need for precise types with code simplicity can be a delicate task.
- Lack of Full Type Safety: While TypeScript enhances type safety compared to JavaScript, it doesn’t eliminate all potential runtime errors. Developers still need to ensure their code logic is correct.
- Development Team Familiarity: If your development team is more experienced with JavaScript or comes from a dynamically typed background, adopting TypeScript may require additional training and adaptation.
- Tooling and IDE Support: While TypeScript-aware tools and IDEs provide valuable features, not all development environments may offer robust TypeScript support.
- Increased Maintenance Overhead: Maintaining type definitions, especially in larger codebases, requires additional effort. As code evolves, type annotations may need to be updated to reflect changes accurately.
- Performance Overhead: The TypeScript compiler adds a layer of abstraction that might impact runtime performance, although this impact is generally minimal and can be outweighed by the benefits of early error detection.
Setting up TS
Setting up a development environment for TypeScript involves a few steps, including installing necessary tools and configuring your project to work with TypeScript.
Tools and installation
Install Node.js and npm:
TypeScript relies on Node.js and its package manager, npm, for various tasks. Install Node.js by downloading the installer from the official website. Once Node.js is installed, npm will be available as well.
Create a Project Directory:
Create a new directory for your TypeScript project and navigate to it using your command line interface.
Installing Typescript
Install the typescript compiler globally
TO verify our installation, we simply type
First TS Program.
Our first Program
In your directory create an index.ts
Since TS is a superset of JavaScript, means all valid JS file is a valid TS File. (index.ts)
Since no browser understand TS, we are going to need to compile the TS code to valid JS code
This will create a new index.js file that will have the transpiled js code for our environment to understand.
TypeScript with ES5
Example of something we definitely don't want in our JS
Above everything seem to work well, but let's take a look at what we got in js
We Certainly don’t want to use a var for this transpilation, what the heck is going on ?
By default TS uses ES5 and that is why we are seeing the var in our code, we certainly wan to change that and that is when we need the configurations.
Configuring the TypeScript Compiler
Configuring the TypeScript compiler (tsc) involves creating a tsconfig.json
file in your project directory. This file specifies compiler options, project settings, and other TypeScript-related configurations.
Creating our configuration file
In our project directory, create a file named tsconfig.json
. This file will hold your TypeScript compiler configuration.
Or simply run the init command to create the TypeScript Starter config file
The Above command will generate a tsc file with a whole lot of commands but we most of the time are interested in a handful of them. in our case here is a few that are kinds interesting
Compiling our TypeScript after configuration
Now that we have added our configuration, we simply can run the tsc command and our compilation will be done for us
Compiling our files after configuration
Debugging TypeScript Applications
To debug our typescript code we can use VSCODE to debug our typescript file.
Fundamentals
Build In types
TypeScript provides a variety of built-in types that you can use to define the shape and behavior of your data. These types help you ensure type safety, catch errors early, and write more robust code. Here are some of the most commonly used built-in types in TypeScript:
Types Scrip types
## JS has a variety of types such as
Declaring our primitive values
Typescript variables
Automatic annotation.
You can also declare variables without type annotation, typescript will do it for you
Since the last one has no type, typescript will assign it the any type
Javascirpt Output
TypeScript variables
TypeScript extends this list by adding new types such as
- The any type
- The Array type
- Unknown
- Never
- Enum
- The tuple type
The any type
#any
The any
type in TypeScript is a special type that represents a value of any type. When you declare a variable with the any
type, you’re essentially telling TypeScript to disable type checking for that variable. This means that you can assign any value to a variable of type any
, and TypeScript won’t raise any type-related errors or warnings.
Here's how the
any
type works in TypeScript:
Drawbacks of any
While the any
type provides flexibility, it comes with drawbacks:
- [i] Type Safety:
- ➡️ By using
any
, you’re essentially opting out of TypeScript’s type system, which defeats the purpose of using TypeScript for type checking and safety.
- ➡️ By using
- [i] Loss of Type Information:
- ➡️ When you use
any
, you lose the benefits of TypeScript’s type inference and type checking, making your code more error-prone.
- ➡️ When you use
- [i] Code Maintenance:
- ➡️ Code written with a lot of
any
types can become difficult to understand and maintain, especially as your codebase grows.
- ➡️ Code written with a lot of
It's generally recommended to avoid using the
any
type as much as possible and instead leverage TypeScript's static typing to ensure code correctness. If you find yourself usingany
frequently, consider exploring more precise types, using type annotations, and making use of type unions and intersection types to maintain better type safety in your TypeScript code.
If you don't want the type annotation error to be turned off you can set the config of the
noImplicitAny
to true, but only do this if you really know what you are doing.
The array type
In TypeScript, the array type is used to define and work with arrays of values. Arrays are collections of items of the same type or a union of different types. TypeScript provides various ways to define and manipulate array types.
Defining your array types
Defining Array Types:
You can define an array type by using the Type[]
syntax, where Type
represents the type of elements in the array.
Alternatively, you can use the
Array<Type>
syntax: also known asArray
generic type:
Array Methods and Properties:
TypeScript provides type-aware support for array methods and properties.
Array Union types
You can use union types to represent arrays that can contain multiple types of values:
Read-Only Arrays:
You can create read-only arrays using ReadonlyArray<Type>
:
This prevents you from modifying the array's contents after creation.
You can also define a readOnly array with mixed types
Array Type Inference:
TypeScript can infer array types when initialized with values:
TypeScript automatically infers the array type based on the provided values.
Array of arrays
Recursive types, are self-referential, and are often used to describe infinitely nestable types. For example, consider infinitely nestable arrays of numbers
You may read or see things that indicate you must use a combination of interface and type for recursive types. As of TypeScript 3.7 this is now much easier, and works with either type aliases or interfaces.
The Tuple type
Tuples are another feature in TypeScript that allow you to represent an array with a fixed number of elements, where each element can have a different type. Tuples provide a way to express a specific structure for your data, ensuring that the correct types are used at specific positions within the array.
We often use them when working with a pair of value
Tuple types allow you to define arrays with fixed lengths and specific types for each element:
To define a tuple, use square brackets and specify the types for each element at their respective positions:
Tuple types enforce the order and number of elements in the array.
Accessing Tuple Elements
You can access tuple elements using zero-based indexing:
As a best practice, restrict your tuple to two values, other than that, you might have trouble working with you data.
Tuple Type Inference:
TypeScript can infer tuple types when initialized with values:
TypeScript automatically infers the tuple type based on the provided values.
Optional Elements
Tuple elements can be optional by using the ?
modifier:
In this case, the second element (number) is optional.
Rest Elements:
You can use the ...
syntax to represent the remaining elements as an array:
Mixing Types:
Tuples allow mixing different types, but you need to ensure that the order and types of elements match:
Limitations:
Tuples provide type safety for fixed-size arrays but are not intended for large datasets or collections. In scenarios where the structure of your data may change or the number of elements is dynamic, other data structures like arrays or objects may be more appropriate.
Benefits:
Tuples are useful when you want to ensure a specific structure and a fixed number of elements at specific positions. They provide better type safety than plain arrays when dealing with mixed data types.
Tuples are often used to represent pairs or small sets of related data, such as coordinates, point values, or function results with multiple types of data
The Enum Type
Enums, short for “enumerations,” are a powerful feature in TypeScript that allow you to define a set of named values. Enums provide a way to create more expressive and self-documenting code by giving meaningful names to specific values
Enum represents a list of related constants
Creating Enums:
You can create an enum using the enum
keyword followed by a list of names (identifiers) representing the possible values.
By default, enum members are assigned numeric values starting from 0,1...
In this example,
Color
is an enum with three members:Red
,Green
, andBlue
.
Custom Enum Values:
You can assign custom values to enum members:
In this case, Active
will have a value of 1, Inactive
a value of 2, and so on. If no value is provided, the first member has a value of 0, and subsequent members are assigned values incrementing by 1.
Accessing Enum Members:
You can access enum members using dot notation:
Reverse Mapping:
Enums in TypeScript have a reverse mapping feature. You can get the name of an enum member from its value:
Enum as Types:
Enums can also be used as types to restrict variable values:
Using enums as types helps prevent accidental assignments of invalid values.
Iterating over Enums:
Since enums are backed by numeric values, you can iterate over them using loops:
String Enums:
String enums allow you to use strings as enum values instead of numeric values:
Const Enums:
Using the
const
keyword with an enum declaration ensures that the enum will not be transpiled into JavaScript, but its values will be inlined where used. This can lead to optimization in certain scenarios.
Use cases of enum
Enums are useful when you have a finite set of related values that represent different options or states. They enhance code readability and maintainability by providing meaningful names to values that might otherwise be represented by numbers or strings
Enums are a great way to model certain types of data, but they might not be the best fit for all scenarios. Be cautious when using enums with a large number of members, as it can lead to bloated code. In summary, enums in TypeScript provide a way to create a set of named values, making your code more expressive and self-documenting. They are especially useful for representing options, states, or categories in your code.
Functions
In TypeScript, you can define the types of parameters and return values for functions. This enhances code clarity and provides type checking:
As a best practice we should always properly annotate our functions, so that all the parameters and return types are properly annotated, especially if you are building an API for other people to use.
Basics Usage
Here, the function
add
takes two parameters of typenumber
and returns a value of typenumber
.
Optional Parameters:
You can make function parameters optional using the ?
modifier:
The
age
parameter is optional, and you can call the function with or without providing it.
Default Parameters:
You can also set default values for parameters:
Function Types:
Functions can be used as types:
This example defines a type
MathOperation
and assigns theadd
function to it.
The Settings to enable when working in type script.
Sometimes it’s just nice to work with the bellow, self explenatory settings
- 💡 noUnusedLocals
- 💡 noUnusedParameters
- 💡 noImplicitReturns
Advanced
We have dealt with function argument and return types, but there are a few more in-depth features we need to cover.
Collable Types
Both type aliases and interfaces offer the capability to describe call signatures:
The thing to not is that on the type Aliases we use
=>
but for the interface we use:
when defining our shape
Let’s pause for a minute to note:
- The return type for an interface is
:number
, and for the type alias it’s=> number
- Because we provide types for the functions
add
andsubtract
, we don’t need to provide type annotations for each individual function’s argument list or return type
void
Sometimes functions don’t return anything, and we know from experience with JavaScript, what actually happens in the situation below is that x will be undefined:
void is a special type, that’s specifically used to describe function return values. It has the following meaning:
The return value of a void function is intended to be ignored😎
We could type functions as returning undefined, but there are some interesting differences that highlight the reason for void’s existence:
Void simply mean that the return type of this value should be ignore , thus do not use it anywhere .
Construct Signature
JavaScript functions can also be invoked with the new operator. TypeScript refers to these as constructors because they usually create a new object. You can write a construct signature by adding the new keyword in front of a call signature:
Construct signatures are similar to call signatures, except they describe what should happen with the new keyword.
Function Overloads
Some JavaScript functions can be called in a variety of argument counts and types. For example, you might write a function to produce a Date that takes either a timestamp (one argument) or a month/day/year specification (three arguments).
In TypeScript, we can specify a function that can be called in different ways by writing overload signatures. To do this, write some number of function signatures (usually two or more), followed by the body of the function:
This type
Sometimes we have a free-standing function that has a strong opinion around what this
will end up being, at the time it is invoked.
We could define
myClickHandler
as follows
Objects type
You can define object types using type annotations:
Interfaces:
Interfaces allow you to define the structure of an object:
Optional Properties:
You can mark properties as optional using ?
:
Now we have set the property to be optional and you can or can not provide it and ts will be okay.
Excess property checking
TypeScript helps us catch a particular type of problem around the use of object literals. Let’s look at the situation where the error arises:
We will get an error such as Object literal may only specify known properties, and ‘color’ does not exist in type
<the type the function expects>
Readonly Properties:
You can make properties read-only using the readonly
modifier:
Index Signatures:
Sometimes we need to represent a type for dictionaries, where values of a consistent type are retrievable by keys. Let’s consider the following collection of phone numbers:
You can define objects with dynamic property names using index signatures:
Clearly it seems that we can store phone numbers under a “key” — in this case home
, office
, fax
, and possibly other words of our choosing — and each phone number is comprised of three strings.
We could describe this value using what’s called an index signature:
Extending Interfaces:
Interfaces can extend other interfaces:
Type Aliases:
You can use type aliases for object types as well:
Advanced types
When it comes to programming languages and type systems, the concepts of structural and nominal types are crucial for understanding how types are defined and compared. Let’s dive into both of these concepts with examples and a deep explanation: Nominal Types: Nominal typing is based on the name or explicit declaration of a type. In languages with nominal typing, two types are considered distinct if they have different names, even if their structures are identical. This means that even if two types have the same structure, if they are defined using different names, they are treated as different types.
Structural Types: Structural typing, on the other hand, is based on the structure or shape of a type rather than its name. In languages with structural typing, two types are considered compatible if their structures match, regardless of their names. This enables more flexible and intuitive type compatibility.
Comparison:
- Nominal typing is more explicit and enforces type distinctions based on names. It can be helpful in preventing unintentional type mixing.
- Structural typing is more flexible and allows for greater type reuse and compatibility based on the structure of types.
Type Alias
TypeScript provides two mechanisms for centrally defining types and giving them useful and meaningful names: Interfaces and type aliases.
A type alias is a way to create a custom name for an existing type or to define a complex type structure. This custom name can then be used throughout your codebase to make your type definitions more expressive and readable. Type aliases enhance the readability of complex type definitions and improve the maintainability of your code.
In TypeScript, the
type
keyword is used to create type aliases, which allow you to define custom names for existing types, unions, intersections, or other complex types. Type aliases make your code more readable, maintainable, and reusable by providing descriptive names for complex types or combinations of types.
Think back to the
: {name: string, email: string}
syntax we’ve used up until this point for type annotations.This syntax will get increasingly complicated as more properties are added to this type. Furthermore, if we pass objects of this type around through various functions and variables, we will end up with a lot of types that need to be manually updated whenever we need to make any changes!
Type aliases help to address this, by allowing us to:
- [p] define a more meaningful name for this type
- [p] declare the particulars of the type in a single place
- [p] import and export this type from modules, the same as if it were an exported value
Basic Usage:
You can use the type
keyword to create a type alias:
In this example,
Age
is a type alias for thenumber
type. This makes your code more meaningful and self-documenting.
Custom Complex Types:
Type aliases can be used to create more complex types:
Here,
Point
is a type alias for an object withx
andy
properties.
Beauty of types
Bellow is our implementation of an employee object,
There are three problems in this implementation
- [c] If we want to create another employee we have to repeat the above shape
- 🔥 which goes again the dry principle
- [c] The second problem is that the other object might have other properties, which means that we are gonna have some kind of inconsistency
- [c] Overall this structure is making our code a bit hard to understand
This is when we use a type alias, using a type alias we can define a custom type
Now we have a single place where we are defining our type and that is what makes the beauty of types, we can also reuse the type we defined
Here’s an example of how we can “cleaned up” an the code from our Union and Intersection Types section (previous chapter) through the use of type aliases:
Inheritance
You can create type aliases that combine existing types with new behavior by using intersection (&) type
While there’s no true extends keyword that can be used when defining type aliases, this pattern has a very similar effect
Interfaces type
Interfaces are a crucial feature in TypeScript that allow you to define contracts for the structure and behavior of objects. They provide a way to specify a set of properties and methods that a class must implement, promoting code consistency, reusability, and type safety.
An interface is a way of defining an object type. An “object type” can be thought of as, “an instance of a class could conceivably look like this”.
Where can they be used?
Since interfaces are used for defining the shape of your object, they can technically be used pretty much on everything you want to have an object shape kind of look (don’t mind the English ,I myself don’t know what I meant.)
For example, string | number is not an object type, because it makes use of the union type operator. an interface can not describe this.
Defining Interfaces:
Interfaces are defined using the interface
keyword. They outline the structure and methods that a class or object should adhere to.
Inheritance in interfaces
- 🔖 Just as in in JavaScript, a subclass
extends
from a base class.- 🔖 Additionally a “sub-interface”
extends
from a base interface, as shown in the example below
- 🔖 Additionally a “sub-interface”
Properties in Interfaces:
- ➡️ Interfaces can include property declarations. Classes implementing the interface must have properties matching the defined names and types.
Methods in Interfaces:
- ➡️ Interfaces can include method signatures. Classes implementing the interface must provide implementations for the methods with matching signatures.
Optional Properties:
- ➡️ You can mark properties as optional using the
?
symbol.
Read-only Properties:
- ➡️ You can make properties read-only by using the
readonly
modifier.
Extending Interfaces:
- Interfaces can extend other interfaces, allowing you to build upon existing contracts.
Implementing a class
TypeScript adds a second heritage clause that can be used to state that a given class should produce instances that confirm to a given interface: implements
.
When working with classes and interfaces, you want to use the implement keyword
what it means bellow is that I have an interface that defines a method or property of my class
The above means make sure that my class has all the animalLike interface require! every instance of dog should make AnimalLike happy.
You can implement as many interfaces as you like.
Union Types
Union types in TypeScript can be described using the |
(pipe) operator.
For example, if we had a type that could be one of two strings, "success"
or "error"
, we could define it as
For example, the flipCoin()
function will return "heads"
if a number selected from (0, 1)
is >= 0.5, or "tails"
if <=0.5.
With union type we can give function parameters more than one type
Type aliases can be used to create union and intersection types:
In this example,
Status
is a type alias for a string literal type, andCombinedPoint
is a type alias for an object that combines properties of thePoint
type and an object with alabel
property.
Discriminative or Tagged union Type.
A discriminative union, also known as a tagged union or discriminated union, is a concept in TypeScript and other programming languages that involves creating a union type where each member of the union includes a specific discriminative property.
This property is used to distinguish between the different possible shapes of the union's instances.
You can refactor the above using type aliases to me the code more readable
Example of another discriminative type union
Intersection Types
Despite being substantially more rare, Intersection type allow you to combine multiple types into a single type that has all the properties and methods of the combined types.
This means that an intersection type includes the characteristics of all the types involved.
Sample combination.
Combining Object Types:
When you combine object types using an intersection type, the resulting type includes properties and methods from all the combined types.
Combining Interfaces:
You can also combine interfaces using intersection types.
Working with methods and properties
An intersection type inherits methods and properties from all the combined types.
Combining Different Types
You can combine object types, interface types, and even primitive types using intersection types.
Intersection represents an object that is made of two or more things
Use case and limitation
Limitations:
While intersection types can be powerful, be cautious about using them excessively, as overly complex types can make your code less readable.
- 🔥 Use Cases:
- ➡️ Combining features from different types to create a new, comprehensive type.
- ➡️ Creating types that are a mix of multiple concepts, such as a person who is also an employee.
Conclusion:
Function Type Aliases:
You can also use type aliases for function signatures:
Here,
MathOperation
is a type alias for a function that takes two parameters of typenumber
and returns a value of typenumber
.
Generic Type Aliases:
Type aliases can be generic, allowing you to define flexible types:
Extending Type Aliases:
Type aliases can be extended to create new types based on existing ones:
Here,
Employee
extends thePerson
type with an additionalrole
property.
Success #UseCases: Type aliases are particularly useful when you want to give meaningful names to complex types or type combinations. They improve code readability, make your intentions clearer, and help prevent errors by enforcing type consistency.
Type literal
sometimes we want to limit the values we can assign to a variable this is when we use the literal type
we want to define a quantity type which can either be 50 or 100?
Literals can also be strings ! they don't have to be numbers only
Comparison with Interfaces:
Type aliases and interfaces have similar purposes, but there are subtle differences.
Type Aliases:
Type aliases are more suitable for creating
- union types,
- intersection types,
- and function type definitions.
- Compatible with Unions, Intersections, and Complex Types: Type aliases are more flexible when it comes to defining complex types, unions, intersections, and mapped types.
- Support for Generics: Type aliases can easily work with generic types, allowing you to create flexible and reusable components.
- Ability to Create Conditional Types: Type aliases can be used to create conditional types that modify the type based on certain conditions.
- Easier to Read and Understand: Type aliases often provide a clear and concise way to define complex types with meaningful names.
When to Use Type Aliases:
- ➡️ Use type aliases when you need to create custom names for complex types, unions, intersections, or mapped types.
- ➡️ Use them for working with generic types and conditional types.
- ➡️ Use them when you want to define a descriptive and meaningful name for a complex type.
Interfaces
Interfaces are more suited for
- defining object shapes
- extending other interfaces.
- Augmentation: Interfaces can be extended or augmented using declaration merging, which allows you to add properties or methods to existing interface declarations.
- Class Implementation: Interfaces are often used to define contracts that classes must adhere to. A class that implements an interface must provide the specified properties and methods.
- Declaration Merging: Interfaces can be merged with other declarations of the same name, which is particularly useful when working with third-party libraries or global declarations.
- Better for Public APIs: Interfaces are generally preferred when defining public APIs for libraries or modules, as they provide a clear contract for consumers. When to Use Interfaces:
- Use interfaces when you need to define the structure that classes or objects must adhere to.
- Use them for extending or augmenting existing interfaces.
- Use interfaces when creating contracts for public APIs or external modules.
- Use them when you want to implement declaration merging to extend existing types.
Top and Bottom types
Defining types Let’s imagine that types describe a set of allowed values that a value might be.
x could be either item from the following set
{true, false}
. Let’s look at another example:
y could be any number. If we wanted to get technical and express this in terms of set builder notation, this would be {y | y is a number}
Let’s look at a few more, just for completeness:
Top Types
A top type (symbol: ⊤) is a type that describes any possible value allowed by the system. To use our set theory mental model, we could describe this as {x| x could be anything }
TypeScript provides two of these types: any and unknown.
any
You can think of values with an any type as “playing by the usual JavaScript rules”. Here’s an illustrative example:
It’s important to understand that any is not necessarily a problem — sometimes it’s exactly the right type to use for a particular situation.
This is an example of where you might want to use an any. in a console log.
Practical use of top types
- You will run into places where top types come in handy very often. In particular, if you ever convert a project from JavaScript to TypeScript, it’s very convenient to be able to incrementally add increasingly strong types. A lot of things will be any until you get a chance to give them some attention.
unknown
is great for values received at runtime (e.g., your data layer). By obligating consumers of these values to perform some light validation before using them, errors are caught earlier, and can often be surfaced with more context.
Bottom Types
A bottom type (symbol: ⊥) is a type that describes no possible value allowed by the system. To use our set theory mental model, we could describe this as “any value from the following set: { } (intentionally empty)”
TypeScript provides one bottom type: never
.
At first glance, this may appear to be an extremely abstract and pointless concept, but there’s one use case that should convince you otherwise. Let’s take a look at this scenario below.
Narrowing with type guards
Introduction to Type Narrowing: Type narrowing is the process of refining or narrowing down the type of a variable based on specific conditions. It allows TypeScript to infer more precise types within conditional blocks, leading to improved type safety and better code understanding.
Type guards are expressions, which when used with control flow statement, allow us to have a more specific type for a particular value.
Why Type Narrowing is Important:
Type narrowing helps prevent errors by enabling the TypeScript compiler to understand the expected type of a variable in different branches of code, based on runtime checks. It improves code correctness and provides more accurate type information to developers.
Types of Type Narrowing:
Typeof Type guard
As we’ve seen, JavaScript supports a typeof
operator which can give very basic information about the type of values we have at runtime. TypeScript expects this to return a certain set of strings:
"string"
"number"
"bigint"
"boolean"
"symbol"
"undefined"
"object"
"function"
Truthiness narrowing
Truthiness might not be a word you’ll find in the dictionary, but it’s very much something you’ll hear about in JavaScript. In JavaScript, we can use any expression in conditionals, &&s, ||s, if statements, Boolean negations (!), and more. As an example, if statements don’t expect their condition to always have the type boolean.
Using built-in
Equality narrowing
TypeScript also uses switch statements and equality checks like =, !, ==, and != to narrow types. For example:
The in
operator narrowing
JavaScript has an operator for determining if an object or its prototype chain has a property with a name: the in operator. TypeScript takes this into account as a way to narrow down potential types.
For example, with the code: “value” in x. where “value” is a string literal and x is a union type. The “true” branch narrows x’s types which have either an optional or required property value, and the “false” branch narrows to types which have an optional or missing property value.
To reiterate, optional properties will exist in both sides for narrowing. For example, a human could both swim and fly (with the right equipment) and thus should show up in both sides of the in check:
Instanceof Type Guard:
The instanceof
operator can be used to narrow down the type of an object based on its constructor function.
Assignments
As we mentioned earlier, when we assign to any variable, TypeScript looks at the right side of the assignment and narrows the left side appropriately.
Custom Type Predicates:
We’ve worked with existing JavaScript constructs to handle narrowing so far, however sometimes you want more direct control over how types change throughout your code.
To define a user-defined type guard, we simply need to define a function whose return type is a type predicate:
Type Assertions:
Type assertions are a way to manually narrow down the type of a variable when you know more about its type than TypeScript can infer.
Discriminated union
Most of the examples we’ve looked at so far have focused around narrowing single variables with simple types like string, boolean, and number. While this is common, most of the time in JavaScript we’ll be dealing with slightly more complex structures.
For some motivation, let’s imagine we’re trying to encode shapes like circles and squares. Circles keep track of their radiuses and squares keep track of their side lengths. We’ll use a field called kind to tell which shape we’re dealing with. Here’s a first attempt at defining Shape.
Notice we’re using a union of string literal types: “circle” and “square” to tell us whether we should treat the shape as a circle or square respectively. By using “circle” | “square” instead of string, we can avoid misspelling issues.
We can write a getArea function that applies the right logic based on if it’s dealing with a circle or square. We’ll first try dealing with circles.
User Defined Type Guards
If we lived in a world where we only had the type guards we’ve seen so far, we’d quickly run into problems as our use of built-in type guards become more complex.
For example, how would we validate objects that are type-equivalent with our
CarLike
interface below?
Validating this type might be possible, but it would almost certainly involve casting.
Even if this did work, it is getting messy enough that we’d want to refactor it out into a function or something, so that it could be reused across our codebase.
Even after doing that delusional type checking mybeCar is still unknown ! despite all we did, it was not enough to tell typescript that mybeCar is actually a carLIke Object!
The solution is simply user defined type.
value is Foo
The first kind of user-defined type guard we will review is an is type guard
. It is perfectly suited for our example above because it’s meant to work in cooperation with a control flow statement of some sort, to indicate that different branches of the “flow” will be taken based on an evaluation of valueToTest’s type.
Pay very close attention to isCarLike’s return type
asserts value is Foo
There is another approach we could take that eliminates the need for a conditional. Pay very close attention to assertsIsCarLike
’s return type:
With the asert the code translate to, if you pass through the function and we haven't thrown, you are car like
Conceptually, what’s going on behind the scenes is very similar. By using this special syntax to describe the return type, we are informing TypeScript that if assertsIsCarLike throws an error, it should be taken as an indication that the valueToTest is NOT type-equivalent to CarLike.
Nullish values
There are situations where we have to plan for, and deal with the possibility that values are null or undefined
Type narrowing is a powerful concept that allows you to refine the type of a variable based on certain conditions. It’s essential for achieving better type safety and making your code more precise.
Although null, void and undefined are all used to describe “nothing” or “empty”, they are independent types in TypeScript. Learning to use them to your advantage, and they can be powerful tools for clearly expressing your intent as a code author.
null
null means: there is a value, and that value is nothing. While some people believe that null is not an important part of the JS language, I find that it’s useful to express the concept of a “nothing” result (kind of like an empty array, but not an array).
This nothing is very much a defined value and is certainly a presence not an absence of information.
Undefined
Undefined
means the value isn’t available (Yet?)
In the example below completed at will be a set at some point but there is a period of time when we haven’t yet set it.
void
We have already covered this in the functions chapter, but as a reminder:
void should exclusively be used to describe that a function’s return value should be ignored
Non-null Assertion Operator
The non-null assertion operator (!.) is used to cast away the possibility that a value might be null or undefined.
Keep in mind that the value could still be null or undefined, this operator just tells TypeScript to ignore that possibility.
By default null and undefined are not enabled and you should check that in the config files to allow them
Optional chaining
When working with nullable objects, we often want to do null checks, and for this, optional chaining is our go to
Optional element access operator
Useful when we are dealing with array, we might have an array of customers, and we want to print the first customer on the console. customers[0]
If the array is null or undefined we must do a check first
This is when we might want to use the optional element access operator.
Yes, we also have an optional call
But the below would surely produce an error because our function is null
This is where we might want to use the optional call
Null Coalescing Operator
Type Assertions
Type assertions (also known as type casting) are a way to tell the TypeScript compiler that you, as a developer, have more information about the type of a value than TypeScript can infer on its own. Type assertions allow you to specify a type for a value, essentially telling TypeScript to trust your judgment.
let's say we want to select an input element on our page using the getElementById, this in typescript will be inferred to an htmlElement which should instead return an htmlInputElement
This is when we might want to tell TS that we know more about our type that it does. we can achieve this using the as keyword
Using the as Syntax:
You can use the as syntax to assert a value’s type:
Here,
(value as string)
tells TypeScript to treatvalue
as astring
type temporarily.
Using the Angle Bracket Syntax (Alternative):
An alternative syntax for type assertions is the angle bracket syntax:
Both syntaxes achieve the same result, but the as syntax is generally recommended, especially when using JSX (if you're using React).
When to Use Type Assertions:
Type assertions should be used carefully, and only when you have a strong reason to believe that you know the type of a value better than TypeScript's type inference. It's important to note that type assertions don't perform any runtime checks or conversions; they only affect how TypeScript analyzes the code.
Avoiding Type Assertion Conflicts:
Be cautious when using type assertions with the any type. Since any disables type checking, assertions might lead to unexpected behavior if not used correctly:
Asserting to Union Types:
You can use type assertions to narrow down union types:
Here, the typeof text === "string" type assertion narrows down the type of text.
User-Defined Type Guards:
Type assertions can be combined with user-defined type guards to achieve more precise type narrowing:
In this example, the
isString
function serves as a type guard, and thevalue is string
assertion is used to narrow down the type.
Success Using
as
with JSX:If you’re using TypeScript with JSX (for frameworks like React), it’s recommended to use the
as
syntax for type assertions, as the angle bracket syntax can conflict with JSX syntax.
The unknown type
The unknown
type is a safer alternative to the any
type in TypeScript. It represents a value about which the TypeScript compiler doesn’t know anything regarding its type.
Unlike
any
, you can't perform arbitrary operations on anunknown
value without narrowing its type first.
Type Safety:
The unknown
type is designed to ensure type safety while working with values of uncertain types. It prevents you from accidentally performing operations that might lead to runtime errors due to incompatible types.
Type Checking:
When you have a value of type unknown
, you can’t directly access its properties or call its methods without first narrowing the type using type assertions, type guards, or other techniques.
Type narrowing
You often need to narrow down the unknown type to a specific type before using it:
In this example, the type of value is narrowed down within the conditions.
Type Assertions with unknown:
You can use type assertions to tell TypeScript about the actual type of an unknown value:
Avoidance of Type Errors:
Using unknown
instead of any
helps you catch type-related errors early in your codebase. It enforces a stronger type checking process and promotes safer programming practices.
Compatibility:
The unknown
type is compatible with all other types in TypeScript. You can assign an unknown
value to any other type, but you need to handle type checks and narrowing before using it.
Difference from any
:
Unlike any
, which disables type checking entirely, unknown
forces you to perform type checks and narrow down the type before performing operations on the value.
Safe APIs
The unknown
type encourages you to create APIs that are safe to use with values of uncertain types. This leads to more robust and maintainable code.
Use Cases:
Use the unknown
type when you’re dealing with values from dynamic sources, such as user inputs or external APIs, and you want to ensure strong type checking without sacrificing flexibility.
The never type
The never
type in TypeScript represents values that will never occur. It’s used to indicate that a function will not return normally or that a variable cannot have any value.
The
never
type is often used in scenarios where an operation leads to an error, an exception is thrown, or an infinite loop is encountered.
Functions Returning never:
A function returning never is one that throws an exception or enters an infinite loop. Such functions are used to indicate that they don’t produce any meaningful value and will never complete normally.
Narrowing Unreachable Code:
The never type is used by TypeScript to narrow types in unreachable code:
Exhaustive Type Checking:
When using the never type in exhaustive type checking, you can ensure that all possible cases have been handled:
Variables with No Possible Value:
Variables that are assigned to the result of functions that never return, or to conditions that are always false, will have the never type:
Difference from void:
The void type indicates that a function doesn’t return a value, while the never type indicates that a function doesn’t return normally and has no reachable end point.
Use Case
Use the never
type when you want to indicate that a function will throw an exception, get into an infinite loop, or that a variable can’t have any possible value.
Object Oriented with typescript.
Defining and Creating Classes
A class in TypeScript is defined using the class
keyword. It acts as a blueprint for creating objects with shared characteristics.
In this example, the Person
class has properties firstName
and lastName
, a constructor to initialize them, and a method greet()
to display a greeting.
Properties and Methods
Properties store data within a class, while methods define the behavior of the class. TypeScript allows you to specify types for both.
Read only and optional properties
Read only property
In Typescript we have modifiers that we can make use of to write robust codes. le’t say we don’t want the id of the bank account to never change
Optional Label
sometimes you want something optional that might or might not be in your class. for that you use the ?
Creating an object or instance.
Once you’ve defined the class, you can create an instance (object) of that class using the new
keyword followed by the class constructor and any necessary arguments.
Check for instance
sometimes you need to check whether an object is an instance of something or not and you can use the instanceof
syntax
Access Object Properties and Methods:
After creating the object, you can access its properties and methods using the dot notation.
You can use the instance variable (in this case, alice
) to access the properties and methods defined within the class.
Access Modifier
ccess modifiers in TypeScript are keywords that determine the visibility and accessibility of class members (properties and methods) from different parts of your code. TypeScript provides three main access modifiers: public
, private
, and protected
.
Public
-
public
Access Modifier:- Members marked as
public
are accessible from anywhere, both within the class and from external code. - It’s the default access modifier if none is specified.
- Members marked as
Private
-
private
Access Modifier:- Members marked as
private
are only accessible within the class they’re defined in. - They can’t be accessed from outside the class, including subclasses.
- Members marked as
Protected
-
protected
Access Modifier:- Members marked as
protected
are accessible within the class they’re defined in and its subclasses (derived classes). - They can’t be accessed from outside the class hierarchy.
- Members marked as
Access modifiers provide encapsulation, allowing you to control how class members are accessed and manipulated from different parts of your code. Using the appropriate access modifier helps ensure that your classes maintain their intended behavior and data integrity.
Parameter Property
Parameter properties in TypeScript are a concise way to declare and initialize class properties directly within the constructor parameters. This feature simplifies the process of defining and initializing properties by combining the parameter declaration and property assignment into a single step.
In this example, the public
access modifier before the constructor parameters firstName
and lastName
creates and initializes corresponding properties within the class. This results in shorter and more readable code compared to declaring properties separately and initializing them in the constructor.
Parameter properties can also use other access modifiers (
private
,protected
, or no modifier), similar to regular class properties.
Using parameter properties can help you create more concise and maintainable code by reducing redundancy and ensuring that properties are correctly initialized when objects are created.
Getter and Setter
In TypeScript, getters and setters are special methods that allow you to define how the properties of a class are accessed and modified. Getters are used to retrieve the value of a property, and setters are used to assign a new value to a property. They provide a way to encapsulate and control access to class properties, enabling you to add additional logic or validation when getting or setting values.
Index Signature
Index signatures in TypeScript allow you to define a "key" and the corresponding "value" type for properties that are not known beforehand.
They enable you to create objects that behave like dictionaries or maps, where you can use arbitrary keys to access values. Index signatures are particularly useful when dealing with objects that have dynamic keys or when working with JSON-like structures.
This just won't work in typescript
Here's how you can use index signatures in TypeScript:
In this example, the Dictionary
type has an index signature
[key: string]: number
, which means you can use any string key to access a corresponding number value. You can use both dot notation (object.key
) and bracket notation (object["key"]
) to access the properties defined by the index signature.
Keep in mind the following points when using index signatures:
- Index signatures can have other properties along with them, but their types must be compatible with the index signature.
- You can use a single index signature per object type.
- While index signatures provide flexibility, they don’t provide type safety or accurate IntelliSense for the keys and values like explicitly typed properties.
Index signatures are a powerful feature in TypeScript for dealing with dynamic data structures and scenarios where you need to work with objects with varying keys.
Class Index signature
You can use index signatures in a class in a similar way to how you use them in regular objects. Here’s how you can implement index signatures within a class:
Static members
Static members in TypeScript are members (properties or methods) that belong to the class itself rather than to instances of the class. These members are shared across all instances of the class and can be accessed using the class name. Static members are often used for utility functions, constants, or methods that don’t require instance-specific data.
Here's how you can define and use static members in TypeScript:
In this example, the MathOperations
class has a static property PI
and a static method calculateArea()
. These members are associated with the class itself, not with instances of the class. You can access them using the class name followed by the member name.
In this example, the static method
createInstance()
is used to create instances of theCounter
class, and the static methodgetCount()
is used to retrieve the count of instances created.
Imagine you're building a simple class to represent Uber rides. You want to keep track of the total number of rides across all instances of the class. This is a perfect scenario for using a static property and a static method.
Inheritance
Inheritance is a fundamental concept in object-oriented programming (OOP) that allows you to create a new class based on an existing class. The new class, known as the “subclass” or “derived class,” inherits properties and behaviors from the existing class, known as the “base class” or “parent class.” TypeScript supports inheritance, enabling you to create class hierarchies and promote code reusability.
Here's how you can implement inheritance in TypeScript:
Key points to remember about inheritance in TypeScript:
- ➡️ The
extends
keyword is used to create a subclass that inherits from a base class. - ➡️ The
super
keyword is used to call the constructor of the base class from within the subclass constructor. - ➡️ Subclasses can override methods of the base class to provide their own implementation.
- ➡️ Subclasses can also introduce new properties and methods in addition to those inherited from the base class.
- ➡️ An instance of a subclass is also an instance of the base class, but the reverse is not true.
- ➡️ TypeScript supports single inheritance, meaning a subclass can extend only one base class.
Inheritance is a powerful tool for organizing and structuring your code, promoting code reuse, and creating class hierarchies that represent relationships between different types of objects.
Method Overriding
Method overriding is a core concept in object-oriented programming that allows a subclass to provide a specific implementation for a method that is already defined in its parent class. In TypeScript, you can override methods inherited from a base class by providing a new implementation in the subclass. This enables you to customize the behavior of a method for specific types of objects.
Polymorphism
Polymorphism is a fundamental concept in object-oriented programming that allows objects of different classes to be treated as objects of a common superclass. It enables you to work with objects in a more generic way, allowing you to write code that can work with various subclasses without knowing their specific types.
In TypeScript, polymorphism is achieved through inheritance and method overriding. Let's use an example to demonstrate polymorphism in TypeScript
We don't have to change our function for our program to work, everything is dynamic and takes another form as you implemented it, and that is polymorphism in action
Open close principle
The Open/Closed Principle (OCP) is one of the SOLID principles of object-oriented design, introduced by Bertrand Meyer. It emphasizes that software entities (such as classes, modules, functions) should be open for extension but closed for modification. In other words, once a module or class is defined and implemented, it should not be modified to add new features. Instead, the module should be extended to accommodate new functionalities.
-
[/] Open for Extension:
- ➡️ You should be able to add new features or behaviors to a module or class without altering its existing implementation.
-
[/] Closed for Modification:
- ➡️ Once a module or class is implemented, its code should not be modified to add new features. Instead, new features should be added through extension.
Abstract Classes and Methods
Abstract classes and methods are advanced features in TypeScript that allow you to define the structure of a class without providing a complete implementation. Abstract classes act as blueprints that other classes can extend, while abstract methods define method signatures that must be implemented by subclasses. They’re particularly useful for creating a common interface for a group of related classes.
Abstract Classes:
- ➡️ An abstract class is a class that cannot be instantiated on its own. It serves as a base for other classes to inherit from.
- ➡️ Abstract classes can have properties, methods (including abstract methods), and constructors.
- ➡️ Abstract classes are defined using the
abstract
keyword before theclass
keyword. - ➡️ Abstract classes can contain both concrete (implemented) methods and abstract (unimplemented) methods.
Another example
In this example,
Animal
is an abstract class with an abstract methodmakeSound()
. TheDog
andCat
classes extendAnimal
and provide concrete implementations for themakeSound()
method.
Generics
Generics in TypeScript allow you to define placeholders for types that are filled in when a function, class, or interface is used. This enables you to create components that work with various types.
What problem are they trying to solve ?
The problem they are trying to solve
We are happy above we have our key value pair well set!
Now the big problem is when you want to use your key as a string !what about that?
With our current implementation we have got two solutions
The first solution is to use any when declaring our type,
This will surely work, but as we already know we don’t really want to use the any type at least the least amount of time possible. And by using any, we simply loose our intellisence Type Safety&checkecking and now typescript is back to being booring.
The other solution is to duplicate our class.
This one simply defies the concept of DRY. and is very redundant, we don’t want to repeat ourselves. and if I want the key to be a Boolean? I will have to
We need to come up with a better way at least to easy the work for ourselves and that is what generics come to solve.
Using generic classes
Multiple generic types
If we don't supply the key value on our constructors, the compiler will infer when compiling.
Generic function
Generic Functions: You can define functions that work with multiple types using generics
Example2:
Generic Class method
You can also have a generic function inside of a class and that would just be alright
Generic Interface
We can also make our interfaces generic!. Interfaces can be defined with generic parameters to work with various types.
Constraints on Generics:
Generics in TypeScript allow you to create components that can work with various types. However, sometimes you want to narrow down the set of types that can be used with a generic. This is where constraints come in. Generic constraints restrict the types that can be used with a generic parameter, ensuring that the types used meet certain criteria.
The Structure of Generic Constraints:
In TypeScript, you can apply constraints to a generic parameter by using the extends
keyword followed by a type. This type serves as the constraint, specifying the allowed types for the generic parameter.
Why Use Constraints:
Constraints are valuable for multiple reasons:
- 💡 Type Safety:
- ➡️ Constraints ensure that the generic function or class operates only on types that satisfy specific criteria, reducing the risk of runtime errors.
- 💡 Method Availability:
- ➡️ Constraints can be used to ensure that specific methods or properties are available on the types used with the generic.
How many constraints can you
Constraint on union type
We have constrained our types to number and string
Constraint On interfaces
Constraint on object shape
Constraints for method availability
Advanced Constraints:
You can create more complex constraints by combining types using intersections, unions, or other type operations.
Typescript Tips and tricks
@ts-expect-error and @ts-ignore Directives @ts-nocheck
@ts-expect-error
and @ts-ignore
are TypeScript directive comments that are used to suppress TypeScript compiler errors or warnings in specific code blocks. While they can be helpful in certain situations, it’s important to use them judiciously, as they can potentially mask real issues in your code.
1. @ts-expect-error
The @ts-expect-error
comment is used to signal to the TypeScript compiler that an error is expected to occur at a certain location in your code. This can be useful when you know that a particular line of code should trigger an error, but you still want the rest of your code to be type-checked correctly.
By using
@ts-expect-error
, you indicate to TypeScript that this error is intentional and should not be treated as a problem.
@ts-ignore
The @ts-ignore
comment is used to instruct the TypeScript compiler to ignore the error or warning that would normally be generated for the following line of code.
While
@ts-ignore
can be helpful in quickly suppressing errors that you know are not problematic, it's also risky because it can hide legitimate issues in your code.
@ts-nocheck
The @ts-nocheck
comment is used to disable TypeScript checking in JavaScript files. It instructs the TypeScript compiler to skip type checking for the entire JavaScript file.
@ts-expect-error-type
The @ts-expect-error-type
comment is used to expect a specific type error in TypeScript. This is similar to @ts-expect-error
, but it allows you to specify the expected error message.
@ts-nocheck
The @ts-nocheck comment is used to disable TypeScript checking for a specific line of code. It’s similar to @ts-ignore, but it only applies to the next line.
While these directive comments can provide flexibility and control over the TypeScript compiler's behavior, it's important to use them thoughtfully and document their purpose clearly to ensure maintainability and code quality.
Typescript utility types
TypeScript utilities are built-in helper types that extend the language’s functionality. They allow you to manipulate and transform types in various ways, making your code more robust and readable. TypeScript utilities come as part of the standard library and are incredibly useful for enhancing your codebase.
Omit utility
Omit utility allows you to copies all properties from the original type except the ones we choose to exclude.
The
Omit
utility is especially handy when you need to create new types by removing certain properties
Pick
The Pick
utility allows you to select a subset of properties from an existing type. Here’s how you can use it:
Partial
The Partial
utility is used to create a type where all properties of an existing type become optional. It’s beneficial when working with optional properties, such as form inputs or configuration objects:
Record
The Record
utility creates an object type with specified keys and a shared value type:
Exclude and Extract
Exclude
and Extract
are two utilities that operate on union types. Exclude
removes values from a union type, and Extract
selects values from a union type:
Required
The Required
utility is the opposite of Partial
. It makes all properties in a type required:
TypeScript Top Bottom Case
In typescript you can double case to a top type such as any
unknown
down to the type of your choice using double types
For you my children who wants simply to suicide yourself, i have got a perfect remedy for you! the double type
What if you are crazy enough to want to use ts but still want to act smart about it and use types that are not supposed to work ?