Warm-up · Activity 1 of 7
// B2.1 · ~30 min · Beginner
Primitives, arrays, any and unknown
After this lesson you can annotate text, numbers, true/false values and arrays, and handle a value of unknown type with unknown and typeof instead of any.
Lesson 1 of 5 in B2 Everyday types
You will be able to
- Annotate values with string, number and boolean, and arrays with number[] or Array<number>
- Explain what any does to type checking, and why strict (noImplicitAny) reports an implicit any
- Use unknown for a value whose type you do not know yet, and check it with typeof before using it
Predict · Activity 2 of 7
Predict before you read on: data is annotated as any. What does npx tsc report?
let data: any = "hello"; data = 42; data.toUpperCase(); const total: number = data;Practice · Activity 3 of 7
Fill in the type: an array of numbers.
const temperatures: ____ = [18.5, 19.25, 21];const temperatures: = [18.5, 19.25, 21];Practice · Activity 4 of 7
value is unknown this time. What does tsc report?
function shout(value: unknown) { return value.toUpperCase(); }Practice · Activity 5 of 7
Match each type to what it allows.
Brain teaser · Activity 6 of 7
Brain teaser. tsc reports no errors for this file. What does node main.ts print?
const settings = JSON.parse('{"volume": 7}'); console.log(settings.volum.toFixed(1));Apply · Activity 7 of 7
Mini-task. Describe an album you like with four annotated constants: a title (string), a year (number), whether you own it (boolean) and its track names (string[]). Then write describe(value: unknown): string, which returns "text: " plus the value when typeof says it is a string, and "not text" otherwise. Print the constants and call describe on a track name and on the year. Run npx tsc, then node main.ts. Finally, change unknown to any, remove the typeof check, and see that tsc no longer complains about value.
Check your work against this list
Build it yourself
Read the worked example, then write the exercises. Your code runs in your browser or on your computer and is never uploaded.
Worked example
A weather station with one unknown input
The station's name, status, readings and labels are annotated with the everyday types. formatReading takes unknown, because a sensor can send anything: it uses value as a number only after typeof has confirmed it is one. Run npx tsc, which prints nothing, then node main.ts. Then delete the if line and its closing brace and run tsc again: it reports error TS18046 at value.toFixed.
main.ts
// A weather station: annotated values, and one input whose type is unknown.
const station: string = "Berlin-Mitte";
const online: boolean = true;
const readings: number[] = [18.5, 19.25, 21];
const labels: Array<string> = ["morning", "noon", "evening"];
function formatReading(value: unknown): string {
if (typeof value === "number") {
return value.toFixed(1) + " degrees";
}
return "no reading";
}
console.log(station, "online:", online);
for (let i = 0; i < readings.length; i++) {
console.log(labels[i] + ":", formatReading(readings[i]));
}
console.log("sensor 4:", formatReading("offline"));
Run it with
npx tsc
node main.tsOutput
Berlin-Mitte online: true
morning: 18.5 degrees
noon: 19.3 degrees
evening: 21.0 degrees
sensor 4: no reading- number[] and Array<string> are the two ways to write an array type; both check every element.
- readings[i] is a number and labels[i] a string, because an element has the array's element type.
- formatReading("offline") type-checks, since unknown accepts anything; the typeof check sends it to "no reading".
- toFixed(1) rounds 19.25 to 19.3 and writes 21 as 21.0.
Change it and run it
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads the TypeScript compiler (up to 2.1 MB) and keeps it cached. Your code stays on your device.
Exercises
Exercise 1 of 2
Fix the playlist
The annotations are right, but tsc reports one wrong element in each array. The third track is called "7", is 2.75 minutes long, and you like it. Fix the three values so that they match their array's type. Leave the annotations and the function as they are. The first test is the type-check.
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads the TypeScript compiler (up to 2.1 MB) and keeps it cached. Your code stays on your device.
Hints
Hint 1
Run npx tsc: each diagnostic names the line and column of one wrong element.
Hint 2
A title is text even when it looks like a number, so it needs quotes. A length is a number, so it must not have them.
Hint 3
The fixed values are "7", 2.75 and true.
Show a solution
One way to solve it. Yours can look different and still pass the checks.
export const titles: string[] = ["Intro", "Blue", "7"];
export const lengths: number[] = [3.5, 4, 2.75];
export const liked: boolean[] = [true, false, true];
export function totalMinutes(values: number[]): number {
let sum = 0;
for (const value of values) {
sum += value;
}
return sum;
}
Run it on your computer
Install TypeScript 7.0 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.ts
export const titles: string[] = ["Intro", "Blue", 7];
export const lengths: number[] = [3.5, 4, "2.75"];
export const liked: boolean[] = [true, false, "true"];
export function totalMinutes(values: number[]): number {
let sum = 0;
for (const value of values) {
sum += value;
}
return sum;
}
main.test.ts
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {titles, lengths, liked, totalMinutes} from './main.ts';
test('The third title is the text "7"', () => {
assert.deepEqual(titles, ['Intro', 'Blue', '7'], 'titles should be ["Intro", "Blue", "7"], all text');
});
test('The playlist lasts 10.25 minutes', () => {
assert.equal(totalMinutes(lengths), 10.25, `totalMinutes(lengths) should be 10.25, got ${JSON.stringify(totalMinutes(lengths))}`);
});
test('liked holds only true and false', () => {
assert.deepEqual(liked, [true, false, true], 'liked should be [true, false, true], without text');
});
package.json
{
"type": "module"
}
tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "nodenext",
"lib": [
"esnext",
"dom"
],
"types": [],
"strict": true,
"noEmit": true,
"erasableSyntaxOnly": true,
"verbatimModuleSyntax": true,
"allowImportingTsExtensions": true
},
"include": [
"**/*.ts"
],
"exclude": [
"**/*.test.ts"
]
}
npx tsc needs TypeScript in the folder: run npm install --save-dev typescript there once. tsc checks the types; Node.js runs main.ts by removing them.
Run the program:
npx tsc
node main.tsRun the checks (needs learnrun.js in the same folder):
npx tsc
node --testDownload learnrun.jsExercise 2 of 2
From any to unknown
describe takes any, so tsc accepts value.length even when value is a number, and describe(42) returns Text of length undefined. Change any to unknown and read what tsc reports. Then add a typeof check, so that text gives "Text of length N" and every other value gives "Not text".
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads the TypeScript compiler (up to 2.1 MB) and keeps it cached. Your code stays on your device.
Hints
Hint 1
With unknown, tsc reports error TS18046: 'value' is of type 'unknown'. That is the point: now it wants a check.
Hint 2
typeof value === "string" is true only for text. Inside that if block, value is a string.
Hint 3
Return "Text of length " + value.length inside the if, and "Not text" after it.
Show a solution
One way to solve it. Yours can look different and still pass the checks.
export function describe(value: unknown): string {
if (typeof value === "string") {
return "Text of length " + value.length;
}
return "Not text";
}
Run it on your computer
Install TypeScript 7.0 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.ts
export function describe(value: any): string {
return "Text of length " + value.length;
}
main.test.ts
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {describe} from './main.ts';
test('Text gives its length', () => {
assert.equal(describe('hello'), 'Text of length 5', 'describe("hello") should be "Text of length 5"');
});
test('A number is not text', () => {
assert.equal(describe(42), 'Not text', `describe(42) should be "Not text", got ${JSON.stringify(describe(42))}`);
});
test('An array is not text either', () => {
assert.equal(describe(['a', 'b']), 'Not text', 'describe(["a", "b"]) should be "Not text"');
});
package.json
{
"type": "module"
}
tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "nodenext",
"lib": [
"esnext",
"dom"
],
"types": [],
"strict": true,
"noEmit": true,
"erasableSyntaxOnly": true,
"verbatimModuleSyntax": true,
"allowImportingTsExtensions": true
},
"include": [
"**/*.ts"
],
"exclude": [
"**/*.test.ts"
]
}
npx tsc needs TypeScript in the folder: run npm install --save-dev typescript there once. tsc checks the types; Node.js runs main.ts by removing them.
Run the program:
npx tsc
node main.tsRun the checks (needs learnrun.js in the same folder):
npx tsc
node --testDownload learnrun.jsCommon mistakes
String with a capital S
const city: String = "Berlin";
const upper: string = city;
console.log(upper);
What tsc or Node.js prints
main.ts(2,7): error TS2322: Type 'String' is not assignable to type 'string'.Why, and the fix
String with a capital S is the type of JavaScript's String wrapper object, not of text. tsc adds a second line: 'string' is a primitive, but 'String' is a wrapper object. Prefer using 'string' when possible. The same goes for Number and Boolean. Write the types in lower case: const city: string = "Berlin".
One number where an array was meant
const ids: number = [1, 2, 3];
console.log(ids);
What tsc or Node.js prints
main.ts(1,7): error TS2322: Type 'number[]' is not assignable to type 'number'.Why, and the fix
number is a single number; an array of numbers is number[]. The message says what was found (number[]) and what the annotation expects (number). Add the brackets: const ids: number[] = [1, 2, 3]. Or leave the annotation out: TypeScript infers number[] from the value.
Using an unknown value without a check
function shout(value: unknown): string {
return value.toUpperCase();
}
console.log(shout("hi"));
What tsc or Node.js prints
main.ts(2,10): error TS18046: 'value' is of type 'unknown'.Why, and the fix
unknown means "any value, and I have not checked which yet", so tsc allows no method, property or arithmetic on it. Check first: if (typeof value === "string") { return value.toUpperCase(); }, then return something else for other values. Do not switch to any to silence the error: that removes the check the error was asking for.
TypeScript in the browser: the TypeScript 6.0.3 compiler, Apache-2.0, then your browser’s own engine. Licence and source
Exit ticket
5 questions, no hints. Score 80% or more to complete the lesson.
Finish every activity above to unlock the exit ticket.