TypeScript Notes To Self : Basic Types
By: Zachary Weiner
Tags: Web Development, Backend Development, Typescript, JavaScript

In TypeScript, Types Are Simply Annotations

TypeScript spits out JavaScript in the end. It’s not forcing types in the JS runtime. So, enforcement of typing isn’t possible since all valid JavaScript files are also typescript files.

TypeScript has all the primitive types of JavaScript (bool, number, string, null, undefined) but it also has some extras to help bridge the gap between soft and strict typed files.

any

Any is exactly like it sounds. It can be anything, anything at all. It’s a strongly typed class that will enforce no rules for construction or for interfaces.

never

never is a method return type. It can be used define a funtion that literally never ends a.k.a. infinite loop

Or only that only ever returns only an error.

void

void is an explicit expectation of no return from a function. Just like in JavaScript if a function doesn’t express a return type, it will return undefined but in strongly typed languages its fairly common to return void explicitly.

Different From C#, and not really better.

Syntax for variable declaration: let instace_name : type_name = ‘Value’

//C#

string my_name = '@DevelopingZack' //TypeScript

let my_name:string = ‘@DevelopingZack’

The difference in syntax may look trivial, but let’s look @ how it actually reads. In C# we could verbally say —

The word 'my_name' means @DevelopingZack

But to re-phrase that in TypeScript it would sound much more like you were speaking with a machine —

Let 'my_name' be a word, and let that word be @DevelopingZack

When the instance is assigned immediately they type can be inferred and we can use something closer to the C# declaration. You should use const for a direct assignment and let for assigning the result of another operation.

const my_name = '@DevelopingZack' //or let my_number = Math.sum(1, 3) //or to create a literal assignment

let my_name: '@DevelopingZack '

REMEMBER: Assigning a literal, can NOT be changed.

It is LITERALLY whatever you assign it to be. They’re great for enums and union return types.

Type Widening

When a variable is left unassigned, that translated to undefined. In TypeScript that gets converted to an any object. This way it doesn’t have to throw an error no matter what happens. Trying to access an undefined objects method? Thats cool, the any object implements no interface so it wont execute, it will return void, and you’ll be on your way!

This is one of the most effective bridges between strictly typed languages and untyped.

Typed Parameters

Parameter types are defined in a method/functions declaration. Where JavaScript only requires a variable name or reference to be used inside a method, in TypeScript we express what type of variable it should be as well.

function distanceTraveled(rate: number, time: number) : number { return rate * time;

}

Union Types

A union a variable that can be more than one type, but not ANY type. Using this strategy we could let our functions accept a string or a number for the variable time.

let time: string|number;

Type Checking

Just like in all other devlopment languages, if we need to narrow the scope of what a variable is we can always execute a call to the instanceof Type Checker

if (elephant1 instanceof Elephant){...}

While this is a pretty good check, it’s not perfect. If the Elephant.new() constructor was not used to create elephant1, while it might still functionally be an Elephant, it can not return true from type checking.

The Type Keyword

Types are all aliases anyway, so typecasting to a new arbitrary type is trivial. The type is not declared in the output script. The declaration of types is simply for code readability and hinting for how code may be implemented.

By declaring the structure of the expected class type, the compiler will notify us if a component of the signature is missing.

type Brand= string;

type Year = string;

type Car = {

make: Brand; //is a string

model: string;

year: year;

} //OR it can be declared directly

let this_car: Car = {

make: 'Toyota',

model: 'Camry',

year: '2020'

}

Marking Fields as Optional (?)

As explained, the complier will complain if the typed object is initialized without all the fields it was expecting. To mark a field as optional, decorate the field definition with a question mark (?)

type Car = {

make: Brand; //is a string

model: string;

year?: Year;

} //will allow for the object to be initialized with the year field as undefined.

Defining Function Signatures as Types

//TODO: Dive further into this.

type functionSignature =

(functionParam: ClassName) => // the signature takes a named

param of an expected type

{array[string]} | null // the signature must return an

array of strings

Types are symbolic for error checking at the compute level. Im curious what advantages can be added to interfaces in TypeScript by using function signatures as Types.

Defining Types as Classes

Types with an explicitly defined class and constructor can mimic the initialization of StronglyTyped languages.

// the declaration as a class object will create the definition and assignments for the fields on the JS output object.

class Car {

constructor(public make: Brand,

public model: string,

public year: Year | null){

}

} const c = new Car(make:'Toyota', model:'Camry');

In the Car class example, all the properties are public. Making them readonly will make sure they are immutable after being set.

This is a declarative advantage to strongly typed languages that would require getters and setters be declared for every field.

We can tag them with the permission modifiers that you would find in C#.

readonly, private, protected, public

Interfaces

JavaScript obviously cant enforce Interfaces but TypeScript can. And although the interfaces aren’t exported to any of the JavaScript output, it can still help at development and compile time.

Interfaces have severe limitations in TypeScript as opposed to Strongly Typed languages. Interfaces that have the same name as the class or instance can create member name clashes.

instanceOf wont work for an object that implements an interface.

Since its not emitted, creates clashes, and wont respond as ‘wrongly-expected’

Note to Self: dont bother with interfaces unless we need dependecy injection.

In TypeScript if two classes have the same fields and methods theyre essentially the same so, you can initialize one as the other — the same way you might benefit from a class that implements some interface.

In JavaScript and TypeScript classes that implement the same members can be used interchangeably. Obviously in JS, anything can be re-assigned to anything else.

Gotcha: The compiler wont complain about assigning objects across classes that have similar members even if theyre not identical. The code suggestions will only suggest the limited scope that the two classes share, but assigning values to fields that dont exist will throw an error at compile time.

Union Typing Custom Types

In a real world scenario you may need to return a number of types depending on the result of an action. To demonstrate we can think of a success or failed result.

export class Request{

requestType = 'REQUEST'

constructor(readonly params: {query : string}){}

} export class SuccessfulRequest{

requestType = 'SUCCESSFUL_REQUEST'

constructor(readonly params: {query : string}){}

} export class FailedRequest{

requestType = 'FAILED_REQUEST'

constructor(readonly params: {query : string}){}

} export type Requests = Request | SuccessfulRequest | FailedRequest;

In the Request example, the member which is common is called the Discriminant.

The discriminant is the shared interface.

interface Desk {

kind: 'desk';

width: number;

height: number;

} interface Chair {

kind: 'chair';

width: number;

height: number;

collapsibility_factor: number;

} type Furniture = Desk | Chair

function shippingSize(furniture: Furniture): number {

switch (furniture.kind){

case 'desk': return shape.height * shape.width;

case 'chair':return(shape.height*shape.width) * collapsibility;

}

} const some_desk: Desk = {kind: 'desk', width: 100, height: 40};

const some_chair: Chair = {kind: 'chair', width: 20, height: 80}; console.log('Desk shipping size:${shippingSize(some_desk)}');

console.log('Chair shipping size:${shippingSize(some_chair)}');

The in Keyword

If the Custom Union type needs to express whether or not the instance implements some member use the keyword in

function arbitraryMethod(furniture: Desk | Chair) {

if ('collapsibility_factor' in furniture){ // doesnt exist in Desk

//do something

}

}

The Unknown Type

Any object can be assigned to any variable. But, in the event where we want to allow that assignment, but require some TypeScript constraints, the unknown type can be assigned.

By assigning a variable to an unknown type, the complier will require the developer to use type narrowing before calling a member on the instance. This will prevent the developer from accessing a method that doesnt exist.

Type Guarding isMyCustomType()

//create a type checking method that takes any variable

//checks if it a typeOf() the expected class

// if => condition is met

// in this case, does the object have a wheels getter?

const isCar = (object:any) : object is Car => !!object && ‘wheels’ if isCar(some_car) {

//do something

} else {

console.log('This is not a car')

}



An easier way to Typeguard:

Create a type that has a property called discriminator and lists the fields and types of variables stored in order for that object to be considered as ‘confirming to the class interface’.

type Car = {

discriminator: 'car';

wheels: number,

doors: number,

make: string,

model: string,

year: date

...

}

This way you can check not for the properties of the object individually, but if the

//object.discriminiator === 'car'

const isCar = (object:any) : object is Car =>

!!object && discriminator === 'car'

Follow Me On Twitter: https://twitter.com/DevelopingZack

Find Me On Telegram: DevelopingZack