TypeScript’s goal is to layer a type system on top of JavaScript. This means if you know JavaScript, you already know a lot of TypeScript.
What you will not know are the extensions to TypeScript in the type-system. This tutorial is a 5 minute overview of the type-system.
Defining Types
You can define a type either through inference, or by explicitly using a type
.
Here is an example of creating an object which has an inferred type which includes name: string
and id: number
:
tsconst user const user: { name: string; id: number; }= { name:(property) name: string"Hayes", id:(property) id: number0 };
An explicit way to describe this object’s shape is via a type
declaration:
tstype User type User = { name: string; id: number; }= { name:(property) name: string string; id:(property) id: number number; };
You can then declare that a JavaScript object conforms to that shape of your new type
by using syntax like : TypeName
after a variable declaration:
tsconst user: User = { name: "Hayes", id: 0 };
TypeScript will warn you if you provide an object which doesn’t match the type you have provided:
tstype User type User = { name: string; id: number; }= { name:(property) name: string string; id:(property) id: number number; }; const user:const user: User User type User = { name: string; id: number; }= { username:(property) username: string"Hayes", Type '{ username: string; id: number; }' is not assignable to type 'User'. Object literal may only specify known properties, and 'username' does not exist in type 'User'.2322Type '{ username: string; id: number; }' is not assignable to type 'User'. Object literal may only specify known properties, and 'username' does not exist in type 'User'. id:(property) id: number0 };
Because JavaScript supports classes and object-orient programming, so does TypeScript - a type declaration can also be used with classes:
tstype User type User = { name: string; id: number; }= { name:(property) name: string string; id:(property) id: number number; }; class UserAccount class UserAccount name:(property) UserAccount.name: string string; id:(property) UserAccount.id: number number; constructor(name:(parameter) name: string string, id:(parameter) id: number number) { this.name (property) UserAccount.name: string= name;(parameter) name: string this.id (property) UserAccount.id: number= id;(parameter) id: number } } const user:const user: User User type User = { name: string; id: number; }= new UserAccount(constructor UserAccount(name: string, id: number): UserAccount"Murphy", 1);
A type can be used to annotate functions in a few ways:
tsfunction getAdminUser(): User { //... } function deleteUser(user: User) { // ... }
There are already a small set of primitive types available in JavaScript: boolean
, bigint
, null
, number
, string
, symbol
, object
and undefined
, which you can use in an interface. TypeScript extends this list with a few more. for example: any
(allow anything), unknown
(ensure someone using this type declares what the type is), never
(it’s not possible that this type could happen) void
(a function which returns undefined
or has no return value).
Composing Types
TypeScript has three ways in which you can build complex types by working with many smaller types.
Unions
A union is a way to declare that a type could be one of many types. For example, you could describe a boolean
type as being either true
or false
:
tstype MyBool type MyBool = boolean= true | false;
Note: If you hover over MyBool
above, you’ll see that it is classed as boolean
- that’s an property of the Structural Type System, which we’ll get to later.
One of the most popular use-cases for union types is to describe a set of string
s or number
s literal which a value is allowed to be:
tstype WindowStates type WindowStates = "open" | "closed" | "minimized"= "open" | "closed" | "minimized"; type LockStates type LockStates = "locked" | "unlocked"= "locked" | "unlocked"; type OddNumbersUnderTen type OddNumbersUnderTen = 1 | 3 | 5 | 7 | 9= 1 | 3 | 5 | 7 | 9;
Intersections
If a union type is an or ( x || y
), then an intersection type is an and ( x && y )
.
TypeScript has intersection types to merge types together:
tstype APIResponse type APIResponse = { success: boolean; error?: { message: string; } | undefined; }= { success:(property) success: boolean boolean; error?(property) error?: { message: string; } | undefined: { message:(property) message: string string }; }; type ArtworksData type ArtworksData = { artworks: { title: string; }[]; }= { artworks:(property) artworks: { title: string; }[] { title:(property) title: string string }[]; }; type ArtistsData type ArtistsData = { artists: { name: string; }[]; }= { artists:(property) artists: { name: string; }[] { name:(property) name: string string }[]; }; type ArtistResponse type ArtistResponse = APIResponse & ArtistsData= APIResponse type APIResponse = { success: boolean; error?: { message: string; } | undefined; }& ArtistsData;type ArtistsData = { artists: { name: string; }[]; } type ArtworkResponse type ArtworkResponse = APIResponse & ArtworksData= APIResponse type APIResponse = { success: boolean; error?: { message: string; } | undefined; }& ArtworksData;type ArtworksData = { artworks: { title: string; }[]; }
Generics
You can get very deep into the TypeScript generic system, but at a 1 minute high-level explanation, generics are a way to provide variables to types.
A common example is an array, an array without generics could contain anything. An array with generics can describe what it holds in the array.
tstype StringArray = Array<string>; type NumberArray = Array<number>; type ObjectWithNameArray = Array<{ name: string }>;
You can declare your own types which use generics:
tstype Backpack<type Backpack<Type> = { add: (obj: Type) => void; get: () => Type; }Type>(type parameter) Type in type Backpack<Type> = { add:(property) add: (obj: Type) => void (obj:(parameter) obj: Type Type)(type parameter) Type in type Backpack<Type>=> void; get:(property) get: () => Type () => Type;(type parameter) Type in type Backpack<Type> }; // This line is a shortcut to tell TypeScript there is a // constant called `backpack`, and to not worry about where it came from declare const backpack:const backpack: Backpack<string> Backpack<type Backpack<Type> = { add: (obj: Type) => void; get: () => Type; }string>; // object is a string, because we declared it above as the variable part of Backpack const object const object: string= backpack.const backpack: Backpack<string>get((property) get: () => string); // Due to this variable, you cannot pass a number to add backpack.const backpack: Backpack<string>add((property) add: (obj: string) => void23); Argument of type '23' is not assignable to parameter of type 'string'.2345Argument of type '23' is not assignable to parameter of type 'string'.
Structural Type System
One of TypeScript’s core principles is that type checking focuses on the shape that values have. This is sometimes called “duck typing” or “structural typing”. This means that if two
---- To be continued ----