An Interest In:
Web News this Week
- April 19, 2024
- April 18, 2024
- April 17, 2024
- April 16, 2024
- April 15, 2024
- April 14, 2024
- April 13, 2024
TypeScript for Beginners
A quick view of the everyday types.
TypeScript has been one interesting thing amongst others that has happened to the JavaScript language in recent years. The superset script has indeed made programming in JavaScript not only safer but much more interesting and approachable (especially to developers coming from languages that use compilers).
It goes without saying that the most errors in JavaScript is the type errors. JavaScript inheritly does not provide a type checking system for its code. so one big implication is that developers tend to provide a different value when a certain kind of value was expected.
This article will cover JavaScript's common data types and the equivalent ways to describe and typecheck them using TypeScript. It will not be touching the advanced parts (not necessarily) of TypeScript. What this article will show is the building block of TypeScript code.
Let's go...
This article is simplified version of the everyday types from the TypeScript docs and assumes you know the fundamentals of javascript.
Installing TypeScript.
TypeScript can be installed in 3 ways: through the NPM registry, as a Visual Studio extension, and through the the .NET package registry (NuGet).
You can chose any routes you want for the installation. I have NodeJS installed so that's what I'll be using.
Visit here to download TypeScript.
If you want to jump straight into code while following, you can use this TypeScript playground.
Everyday Types
The essence of TypeScript is provide make for what the language lacked the most a static typechecker. A typechecker runs before the actual code runs to ensure all data types are correct (typechecked) and are used as they should in a program.
Annotating Primitives
JavaScript has three basic primitives data types: string
,number
and boolean
. These form the basic level types in the language.
- String represent text like
"Hi Romeo"
. - Number represent number like
5
. Every digit in JavaScript represented asnumber
. There aren't special representation likeint
andfloat
- Bolean represent two values,
true
andfalse
.
Below is the representation of primitves in JavaScript and the equivalent in TypeScript.
// JavaScriptconst name = "Romeo";const age = 23;const isChristian = true;
// TypeScriptconst name: string = "Romeo";const age: number = 23;cont isChrisian: boolean = true;
NOTE:
TypeScript is capable of inferring types automatically in your code. For example, the type of a variable is inferred based on the type of its initializer:
let myName = "Alice";
No type annotation needed 'myName' inferred as type 'string'
Arrays
The represent types of an array, say a string of names like ["Romeo","Uwan',"Peter"]
. you can use the string[]
sytax to do so. It works for other types as well, like numbers (number[]
).
// JavaScriptconst names = ["Romeo", "Uwan", "Maxwell", "Peter"];const numbers = [23, 5.5, 42, 32];
// Typescriptconst names: string[] = ["Romeo", "Waan", "Maxwell", "Peter"];const numbers: number[] = [23, 5.5, 42, 32];
Any
The any
type is special to TypeScript and will cause a value not be validated by TypeScript for its type. In other words, it won't be typechecked.
When a value has the any
type, it and its property can be accessed and manipulated as you normally would in JavaScript without it's being typechecked. That means it can be assigned to (or from), called as a function and it's property, which in turn has type of any
, can be accessed.
// Typescriptlet someVariable: any = {name: "Romeo"};someVariable.bar = 100;someVariable = (n=10) => n;someVariable();
NOTE:
Theany
type can be disabled using thenoImplicitAny
flag. You'll learn more about TypeScript configuration as we proceed.
<-- Describe noImplicitAny
in .ts
configutation file -->
Functions
Function annotation in TypeScript are of two types: Parameter Type Annotation and Return Type Annotation.
Parameter Type Annotation
When you declare a function you can annotate the parameters to indicate types of parameters the function expects. parameter type annotation come after the parameter is declared, like so:
// JavaScriptfunction greetRomeo(name) { console.log("Hey Romeo, I'm " + name);}
// TypeScriptfunction greetRomeo(name: string) { console.log("Hey Romeo, I'm " + name);}
Any argument passed to the greetRomeo
function will be checked. In the case above, the name
parameter expect a string agument, any thing else, say a number, will show an error.
NOTE:
TypeScript is strict about the number of parameters passed to a function. Even without annotation, it'll check that you passed in the right number arguments.
Return Type Annotation
You can annotate the returned value from a function. Anything value return that doesn't match type annotated will be flag for error.
JavaScriptfunction getAge() { return 23}
TypeScriptfunction getAge(): number { return 23}
Typescript can infer the return type based on the value return. Annotating the type value is mostly for documentation purposes.
Personally, I take documentation quite seriously. Fellow developers shouldn't have to peel their scalp to understand what codebase does.
Anonymous function
When a function is a passed as a callback (most often anonymous functions), TypeScript can determine how that function will be called thus inferring the paramater type for such functions.
// No type annotations here, but TypeScript can spot the bugconst names = ["Romeo", "Waan", "Peter"];// Contextual typing for functionnames.forEach(function (s) { console.log(s.toUppercase()); // Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?});// Contextual typing also applies to arrow functionsnames.forEach((s) => { console.log(s.toUppercase()); // Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?});
Notice that the s
parameter for both functions pure and arrow is not annotated, yet TypeScript could infer the correct type because it knows how the anonymous function will work on an array in that context. This is called contextual typing because the context where the function is used is known to TypeScript thus it (TypeScript) can infer the type the function should have.
Object types
Beside primitives, a common form of data type you deal with as a JavaScritp developer is an Object; this is any JavaScript data type with properties and values.
To define an object type, you list the properties and their types. For instance, here's a function that takes an object as a parameter.
// JavaScriptfunction romeosProfile(profile){ if (typeof profile === "Object") { console.log("Romeo's age is " + profile.name); console.log("Romeo's height is " + profile.height); }}romeosProfile({name: "Romeo", age: 23});
// TypeScript// The parameter's type annotation is an object typefunction romeosProfile(pt: {name: string, age: string}){ console.log("Romeo's age is " + pt.name); console.log("Romeo's height is " + pt.height);}romeosProfile({name: "Romeo", age: 23});
The parameter of the function is annotated as an object type. the further annotation of the object type itself is optional which, if done so, will have a type of any
.
Optional properties
Object types can specify some optional properties by appending a ?
after the property name.
function romeosProfile(obj: {name: string, age?: number}) { // ...}// Both OKprintName({ name: "Romeo" });printName({ name: "Romeo", age: 23 });
Say you didn't provide an argument for the optional parameter and you accessed it, it will return undefined
because the property doesn't exist. When reading from an optional property, make sure to check that it's not undefined
.
function romeosProfile(pt: {name: string, age?: number}) { // Error - might crash if 'pt.age' wasn't provided! console.log(pt.age); if (pt.age !== undefined) { // OK console.log(pt.age); } // A safe alternative using modern JavaScript syntax: console.log(pt.age?);}
Combining Types in TypeScript
The type system in TypeScript allows you to combine and form new types from existing ones.
Union Type
A union type is a type formed by combing two or more other types that represent values of any of the existing types. the combine types are referred to as the union's member.
Here is function that accept and string and number as it's parameter.
function printAge(age: number | string) { console.log("i'm " + " years old");}printAge(23) // I'm 23 years oldprintAge("23") // I'm 23 years oldprintAge({age: 23}) // Error
Working with Union Type
TypeScript will only allow an operation if it's valid (intersection of type property) for either of the union member. For instance, you can't perform a toUpperCase
operation on a string | number
union and that's because the operation is only valid for value with type of string
but the type value could be a number
.
To get around union type who's property may not intersect, you use Narrowing to "... deduce a more specific type for a value based on the structure of the code."
For instance, using a conditional to let TypeScript know that an operation for a certain type.
function printAge (age: number | string) { if (typeof age === "string") { console.log(age.toUpperCase()); } else { // age is type of 'number' console.log(age) }}
or if member of union type is an array:
function meetFriends(x: string[] | string) { if (Array.isArray(x)) { console.log("Hello, " + x.join(" and ") + ". Nice to meet you all!") } else { // is a string and not an array console.log("Hello " + x); }}
As indicated earlier, members of a union type whose value share (intersect) properties don't need narrowing:
function getFirstTime(x: number[] | string) { return x.slice(0, 3);}
Type Aliases
Type aliases allows you to name a type and use it more than once by only referring to it by name. It is a name for any type.
Object aliase typetype Profile = { name: string, age: number}// Union Type aliase typetype ID = number | string;
With the Profile
type Aliase above, you can pass it as parameter type for a function by simping referring to the name (Profile
).
// Type Profile creates a type structure to be used as parameter typefunction getProfile(pt: Point) { console.log("Name: " + pt.name); console.log("Age: " + pt.age);}getProfile({name: "Romeo", age: 23});
Interfaces
Interfaces are similar to types aliases, and the two can be used interchangeably to create a named type for an object. The only difference is that a type aliase can't be added new fields when once created, compared to interface that is opened to be added more fields.
Object interfaceinterface Profile = { name: string, age: number}// Valid and will be combined with above interfaceinterface Profile = { eyeColor: string}// Type Profile creates a type structure to be used as parameter typefunction getProfile(pt: Point) { console.log("Name: " + pt.name); console.log("Age: " + pt.age);}getProfile({name: "Romeo", age: 23});
It is generally preferred to use interface
when creating a named type for objects, just in case you'd want to add to the existing fields.
interface Profile = { name: string, age: number, eyeColor: string}// Object takes the structure of defined interface using Type Assertionconst RomeosProfile: <Profile> { name: "Romeo", age: 23, eyeColor: "black"}
Type Assertions
Type assertion allow you to declare (assert) a type on a variable so that the compiler won't infer it at runtime. This is because you, as the programmer, could have more information of the type of value that TypeScript can't or at least, it'll infer something not quite right.
Type assertion is similar to typecasting in other languages like C++, C# and java except that there is no runtime effect (all assertions are removed by the compiler) in TypeScript.
for instance, if you're accessing the DOM using document.getElementbyId
, TypeScript understands that it'll return a HTMLElement
, but you might know specifically that it'll be a HTMLCanvasElement
.
You can use a type assertion to specify that:
const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;
You can use the angle-bracket to achieve the same effect.
const myCanvas = <HTMLCanvasElement>document.getElementById("main_canvas");
Another instance is asserting object, similar to the one you saw earler.
interface Creator { name: string; code: number; }// Using the `as` keywordlet person = {} as Creator; // Okperson.name = "Romeo";person.age = 23;// Using angle-bracketlet person = <Creator> {}; // okperson.name = "Romeo";person.age = 23;
NOTE:
When dealing with TypeScript in React (
.tsx
format), ony theas
keyword is allowed because using angle-brackets in JSX will create a conflict in the React code.
Literal Types
With literal types you can create and refer to specific string and number in type positions.
For instance, a variable with a specific string type:
let x: "hello" = "hello";x = "Howdy"; // Type '"howdy"' is not assignable to type '"hello"'.
When combined to form a union, literals can be used to create complex and useful type structure in type positions.
for instance, a funtion with a second paramter that only accepts certain values.
function creator(age: number, name: "Romeo" | "Waan" | "Peter"): void { console.log(alignment);};creator(23,"middle"); // Argument of type '"middle"' is not assignable to parameter of type '"Romeo" | "Waan" | "Petercreator(23, "Waan") // No error, will log `Waan`
Also, a function that can only return numerical literals:
function compare(a: string, b: string): 1 | -1 | 0 { return a === b ? 0 : a < b ? 1 : -1;}compare("23", "34"); // No error, will log 1;
Literal types can be combined with non-literal types too.
interface Option { width: number;}function configure(x: Options | "auto") { // ...}configure({ width: 100 }); // No errorconfigure("auto"); // No errorconfigure("automatic"); // Error: Argument of type '"automatic"' is not assignable to parameter of type 'Options | "auto"'.
Although we didn't use it, the boolean (true
and false
) literal type can used to achieve similar concept as the ones abovel.
NOTE:
The
boolean
type is actually an aliase that represent the union oftrue
andfalse
.
Alrigth, that's about it for the everyday types you'll encounter while using or reading TypeScript code. Before I round up things, let's look into configuring TypeScript using the tsconfig.json. file
TS Config
The TypeScript configuration file is tsconfig.json
that sits at the root of project. It's automatically created when the TypeScript is first initialized. The file specifies the root files and compiler options for the project.
Using the .tsconfig
to compile project.
There are two ways you can do this:
- By invoking the
tsc
CLI command with no input files, in which case the compiler uses thetsconfig.json
(starting from the current directory and going the the directory chain) file to look for the project file to compile. - By invoking the
tsc
CLI command with a--project
or-p
option that specifies the directory to the.tsconfig.json
file containing the configuration.
When input files are specified on the command line, the ones specified in the tsconfig.json
files are ignored.
Here is a reference to the configuration options you can make as fitting to a project.
Conclusion.
The basis of this article is get you started with building block of TypeScript code. The TypeScript documentation answers questions this article fails to provide. Do check it out.
Let's connect on Twitter, I hear that my tweets are quite intriguing.
Cheers!
Original Link: https://dev.to/romeopeter/typescript-for-beginners-50h0
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To