Barebones TypeScript
A Pre-Interview Recap of TypeScript
Introduction
This article helps you if you’re already familiar with TypeScript and you need to:
- Prepare for interviews, or
- 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)); // nullo(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)); // undefinedfunction z(a: number | null) : number {
if (a != null) return a * 2;
return 0;
}o(z(1)); // 2
o(z(null)); // 0function 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); // undefinedlet 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); // 1o(X[X.A]); // 'A'
o(X[X.B]); // 'B'enum Y { C = 2, D = 3 }o(Y.C); // 2
o(Y.D); // 3enum Z { E, F = 5, G }o(Z.E); // 0
o(Z.F); // 5
o(Z.G); // 6enum 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 👍