Barebones TypeScript

A Pre-Interview Recap of TypeScript

George Marklow
5 min readJul 26, 2022
Photo by Steve Johnson on Unsplash

Introduction

This article helps you if you’re already familiar with TypeScript and you need to:

  1. Prepare for interviews, or
  2. Reacquaint yourself with the language after a period away from TypeScript e.g. moving from backend development to a front-end project.

Each part consists of a “drill” that explores important operations of a few common TypeScript operations.

Names of variables and methods are kept deliberately short to minimize the amount of typing required to complete the code exercises.

What This Article Is Not

This article is about “learning by doing” and doesn’t offer thorough explanations of the operations used. You’re left to explore these operations in more detail on the TypeScript Documentation page in your own time.

Setup

Visit the TypeScript Playground.

Add the following line to the TypeScript playground code area, a shorthand method to print a result:

function o(x: any) {
console.log(x)
}

Add code, switch to the Logs view on the right-hand side, then click the Run button to see the results:

In the code below, whenever you see a ❌, this is a compile-time error and is designed to teach you about important TypeScript restrictions. You can comment this out using two forward slashes before the line of code like so:

// p = 3;                            

Types

Restricting Values

let a: number | string;
a = 1
a = '1'
let p: 1 | 2 = 1; //
p = 2; //
p = 3; // ❌
function x(a: boolean, b: number): string | number | null {
if (b === 0) return null;
return a ? b.toFixed(2) : b;
}
o(x(true, 1)); // "1.00"
o(x(false, 1)); // 1
o(x(false, 0)); // null
o(typeof x(true, 1)) // "string"
o(typeof x(false, 1)) // "number"
o(typeof x(false, 0)); // "object"
x(false, 1) as boolean; // ❌

Array Type Checking

let x = [1,2];
x.push(...[3, 4])
o(x) // [1, 2, 3, 4]let y : any[] = [1,2];
y.push(...[3, '4'])
o(y) // [1, 2, 3, "4"]

Creating a Type

type q = 'A' | 'B';                  //
type r = `Letter ${q}`; //
let a: r = 'Letter A'; //
let c: r = 'Letter C'; // ❌

Functions

Optional Values and Spread Operator

function x(a: number = 1, ...b: number[]) {
return a + b.reduce((total, val) => total + val, 0);
}
o(x(2)); // 2
o(x()); // 1
o(x(2, 3, 4)); // 9

Restricting Return Values

function y(a: number | null) {
if (a != null) return a * 2;
return undefined;
}
o(y(1)); // 2
o(y(null)); // undefined
function z(a: number | null) : number {
if (a != null) return a * 2;
return 0;
}
o(z(1)); // 2
o(z(null)); // 0
function f(n: number, s: string) : `Letter: ${'A' | 'B'}` { if (n === 1) return 'Letter: A';
if (n === 2) return 'Letter: B';
return 'Letter: Z'; // ❌
}

Overloading Functions

function f(a: number): number;
function f(a: null): null;
function f(a: number | null): number | null {
if (a !== null) return a * 2;
return null;
}
o(f(1)); // 2
o(f(null)); // null

Assert

function g(a: boolean) : asserts a {
if (!a) throw new Error('False');
}
o(g(false)); // "Executed JavaScript Failed:"
// "False"
function h(a: any) : asserts a is number {
if (typeof a !== "number") throw new Error('NaN');
}
o(h('z')); // "Executed JavaScript Failed:"
// "NaN"

Tuples and Enums

Tuples

let t: [number, string] = [1,'a'];t.forEach((a: string | number) => {
if (typeof a === 'string')
o(`String ${a}`);
else if (typeof a === 'number')
o(`Number ${a * 2}`);
}) // "Number 2"
// "String a"
let [a,b] = t;
o(a); // 1
o(b); // "a"
let u: [number, string, number?] = [1,'a', undefined];let [c,d,e] = uo(c); // 1
o(d); // "a"
o(e); // undefined
let v: [number, string, ...number[]] = [1,'a', 2, 3];let [f,g,h,i] = vo(f); // 1
o(g); // "a"
o(h); // 2
o(i); // 3

Enums

enum X { A, B }
o(X.A); // 0
o(X.B); // 1
o(X[X.A]); // 'A'
o(X[X.B]); // 'B'
enum Y { C = 2, D = 3 }o(Y.C); // 2
o(Y.D); // 3
enum Z { E, F = 5, G }o(Z.E); // 0
o(Z.F); // 5
o(Z.G); // 6
enum P { A = 'S', B = 'T' }o(P.A); // 'S'
o(P.B); // 'T'
const enum R { A, B }o(R.B); // 1
o(R['B']); // 1
o(R[0]); // ❌

Shape Types, Unions, and Type Guards

Shape Types

let x = { a: 'x.A', b: 'x.B' };
let y = { a: 'y.A', b: 'y.B' };
let z: { a: string, b: string }[] = [x, y];
z.forEach(s => o(`${s.a} ${s.b}`)); // "x.A x.B"
// "y.A y.B"
let f = { a: 'f.A', b: 'f.B' };
let g = { a: 'g.A', b: 'g.B', c:'g.C' };
let h: { a: string, b: string, c?: number }[] = [f, g];
h.forEach(s => o(`${s.a} ${s.b} ${s.c}`)); // "f.A b.B undefined"
// "g.A g.B g.C"

Union Types

type X = {
a: number,
b: string
};
type Y = {
a: string,
b: string
};
type U = {
a: number | string,
b: string
};
let p = { a: 1, b: 'p.B' };
let q = { a: '2', b: 'p.B' };
let pq: U[] = [p, q];pq.forEach(item => o(`${item.a} ${item.b}`)); // "1 p.B"
// "2 p.B"

Type Guards

type X = {
a: number,
b: string,
c?: number
};
type Y = {
a: string,
b: string,
d?: number
};
let p = { a: 1, b: 'p.B', c: 10 };
let q = { a: '2', b: 'p.B', d: 20 };
let pq:(X | Y)[] = [p, q];pq.forEach(item => {
if ('c' in item) {
o(`${item.a} ${item.b} ${item.c}`) // "1 p.B 10"
}
else if ('d' in item) {
o(`${item.a} ${item.b} ${item.d}`) // "2 p.B 20"
}
});

Type Intersection

type S = {
a: number,
b: number,
};
type T = {
c: number,
d: number
};
let m = { a: 1, b: 2, c: 10, d: 20 };let items: (S & T)[] = [m];items.forEach(item => {
o(`${item.a} ${item.b} ${item.c} ${item.d}`) // "1 2 10 20"
});

Thanks for reading! Let me know what you think in the comments section below, and don’t forget to subscribe 👍

--

--

George Marklow
George Marklow

Written by George Marklow

George is a software engineer, author, blogger, and abstract artist who believes in helping others to make us happier and healthier.

No responses yet