Understanding TypeScript Types: Primitives, Objects, and Type Manipulations
Rajesh Rathore
Posted on July 19, 2023
TypeScript Types
TypeScript is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale. TypeScript types are categorized into primitive types and object types. The basic types in TypeScript include string, boolean, number, array, tuple, and enum. TypeScript's type system allows you to build new types out of existing ones using a large variety of operators.
There are several types that you can use to define variables. Here are some commonly used types:
Primitives Types:
- number: Represents numeric values like integers, floats, etc.
- string: Represents textual data enclosed in single quotes ('') or double quotes ("").
-
boolean: Represents a logical value, either
true
orfalse
. - null: Represents the absence of any object value.
- undefined: Represents an uninitialized variable or an object that lacks a value.
- symbol: Represents unique and immutable values that can be used as identifiers.
Object Types:
- object: Represents a non-primitive type, i.e., anything that is not a number, string, boolean, null, or undefined.
- array: Represents an ordered list of values of a specific type. You can define an array using square brackets ([]).
- tuple: Represents an array with a fixed number of elements, where each element can have a different type. Tuples allow you to specify the type of each element in a specific order.
- enum: Represents a set of named constants. Enums allow you to define a collection of related values that can be assigned to a variable.
- function: Represents a function type. You can define the parameter types and return type of a function using function types.
Other Types:
-
any: Represents a dynamic type that can hold any value. When a variable is of type
any
, TypeScript doesn't perform type checking for that variable. -
unknown: Represents a type-safe counterpart of
any
. Variables of typeunknown
can hold any value, but TypeScript enforces type checking before performing any operations on them. - void: Represents the absence of any type. Typically used as the return type of functions that don't return a value.
- never: Represents a type for values that never occur. It is used for functions that don't have a reachable endpoint or for variables that cannot have a value.
-
literal types: Represents a specific value that a variable can hold. For example, you can define a variable of type
'success'
, which can only have the value'success'
.
These are some of the commonly used types in TypeScript. Understanding and utilizing these types can help you write more robust and type-safe code.
Type Assertion
Type Assertion is used in TypeScript to override the type inferred by the compiler. It is a mechanism that tells the compiler about the type of a variable. In TypeScript, type assertions can be performed using the "angle bracket" syntax or the "as" keyword.
Here's an example of Type Assertion in TypeScript:
let code: any = 123;
let employeeCode = <number> code;
console.log(typeof(employeeCode)); // Output: number
In this example, we have a variable code
of type any
. We assign the value of this variable to another variable called employeeCode
using type assertion. The compiler is informed that code
is of type number
. The typeof
operator confirms that employeeCode
is indeed of type number
.
Non-null Assertion
The non-null assertion operator (!) tells the TypeScript compiler that a value typed as optional cannot be null or undefined. It is placed after the variable name and is used to assert that the variable is not null or undefined.
Here's an example of how to use the non-null assertion operator in TypeScript:
let word: string | null = null;
const num = 1;
if (num) {
word = "Hello World!";
}
console.log(word!.toLowerCase());
In this example, the variable word
is of type string
or null
. The non-null assertion operator (!
) is used to assert that word
is not null. Therefore, we can safely call the toLowerCase
method on word
.
Type Inference
Type inference is used in TypeScript to provide type information when there is no explicit type annotation. It occurs when initializing variables, setting parameter default values, and determining function return types. TypeScript uses the best common type algorithm to select the best candidate types that are compatible with all variables. TypeScript also uses contextual typing to infer types of variables based on their usage in the code.
Here's an example of how type inference works in TypeScript:
let x = 3;
In this example, the type of the variable x
is inferred to be number
because it is initialized with a numeric value.
Type Compatibility
TypeScript uses a structural type system, where a type is compatible with another type if it has at least the same members. TypeScript's type compatibility is based on the structure and shape of types rather than their explicit declarations.
Here's an example of type compatibility in TypeScript:
interface Pet {
name: string;
}
let pet: Pet;
let dog = { name: "Lassie", owner: "Rudd Weatherwax" };
pet = dog;
In this example, the Pet
interface defines a property name
. We declare a variable pet
of type Pet
and assign it an object dog
with a name
property. TypeScript considers the object dog
to be compatible with the Pet
interface because it has at least the same members.
Combining Types
In TypeScript, combining types refers to the ability to create new types by combining existing types in various ways. These combinations can be done using union types, intersection types, and conditional types. Let's explore each of these combining types in TypeScript:
Union Types
Union types allow you to combine multiple types using the |
(pipe) symbol.
A value of a union type can be of any of the constituent types. For example:
type MyType = string | number;
let x: MyType;
x = "hello"; // valid
x = 10; // valid
x = true; // invalid, as boolean is not in the union
Union types are useful when a value can be one of multiple types.
Intersection Types:
Intersection types allow you to combine multiple types into a single type that has all the properties and methods of each constituent type. It is denoted by the &
(ampersand) symbol. For example:
type FirstType = { name: string };
type SecondType = { age: number };
type CombinedType = FirstType & SecondType;
let obj: CombinedType = { name: "John", age: 25 };
console.log(obj.name); // "John"
console.log(obj.age); // 25
Intersection types are useful when you want to create a type that has the combined features of multiple types.
Conditional Types:
Conditional types introduce type transformations based on a condition. They are defined using the extends
keyword and are denoted by the ?
(question mark) symbol. Conditional types are evaluated dynamically based on the given type parameter. For example:
type NonNullable<T> = T extends null | undefined ? never : T;
let value: NonNullable<string | null>;
value = "hello"; // valid
value = null; // invalid
let numberValue: NonNullable<number | undefined>;
numberValue = 10; // valid
numberValue = undefined; // invalid
In this example, the NonNullable
conditional type transforms the given type parameter by excluding null
and undefined
types.
These combining types provide powerful ways to create complex types in TypeScript, allowing you to express a wide range of scenarios and enforce type safety in your code.
Type Guards and Narrowing in TypeScript
Type guards in TypeScript allow you to narrow down the type of a value within a conditional block based on a specific condition. Type narrowing is the process of reducing the possible types of a value within a certain scope. This helps you write more precise and type-safe code. Here's an example that demonstrates type guards and narrowing:
interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
function getArea(shape: Shape): number {
if (shape.kind === "circle") {
// Type guard narrows down the type to Circle
return Math.PI * shape.radius * shape.radius;
} else if (shape.kind === "square") {
// Type guard narrows down the type to Square
return shape.sideLength * shape.sideLength;
} else {
// This code is unreachable because Shape only allows Circle or Square
throw new Error("Invalid shape");
}
}
const circle: Circle = { kind: "circle", radius: 5 };
console.log(getArea(circle)); // Output: 78.53981633974483
const square: Square = { kind: "square", sideLength: 4 };
console.log(getArea(square)); // Output: 16
In this example, we have defined two interfaces, Circle
and Square
, both of which have a kind
property indicating the type of the shape. The Shape
type is a union of Circle
and Square
. The getArea
function takes a Shape
parameter and uses type guards to narrow down the type within the conditional blocks.
When we call getArea
with a Circle
object, the type guard shape.kind === "circle"
allows TypeScript to infer that the shape
parameter is of type Circle
within that block. Similarly, when shape.kind === "square"
, TypeScript knows that the shape
parameter is of type Square
. This narrowing enables us to access the specific properties of each shape without type errors.
Type guards are a powerful feature in TypeScript, allowing you to write more reliable and type-safe code by narrowing down the possible types of values based on conditions.
š Thank You for Joining the Journey! š
I hope you found this blog post informative and engaging. Your support means the world to me, and I'm thrilled to have you as part of my community. To stay updated on my latest content.
š Follow me on Social Media! š
š Visit my Website
š¢ Connect with me on Twitter
š· Follow me on Instagram
š Connect on LinkedIn
š Check out my GitHub
š A Special Message to You! š
To all my dedicated readers and fellow tech enthusiasts, I want to express my gratitude for your continuous support. Your engagement, comments, and feedback mean the world to me. Let's keep learning, growing, and sharing our passion for development!
š„ Let's Stay Connected! š„
If you enjoy my content and want to stay in the loop with my latest posts, please consider following me on my social media platforms. Your support is invaluable.
Thank you for being a part of this amazing journey! š
Posted on July 19, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.