0% found this document useful (0 votes)
3 views18 pages

Module 03.1 Trusting TypeScript

This document discusses the importance of testing in software engineering, particularly in the context of TypeScript and its type system. It emphasizes the need to write tests that respect function contracts and highlights the differences between the 'any' and 'unknown' types in TypeScript. The document also introduces the Zod library for validating inputs in a web server context, showcasing how it can improve type safety and error handling.

Uploaded by

panther
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views18 pages

Module 03.1 Trusting TypeScript

This document discusses the importance of testing in software engineering, particularly in the context of TypeScript and its type system. It emphasizes the need to write tests that respect function contracts and highlights the differences between the 'any' and 'unknown' types in TypeScript. The document also introduces the Zod library for validating inputs in a web server context, showcasing how it can improve type safety and error handling.

Uploaded by

panther
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

CS 4530: Fundamentals of Software Engineering

Module 3.1: Trusting TypeScript (or not!)

Adeel Bhutta, Rob Simmons, and Mitch Wand


Khoury College of Computer Sciences

© 2026 Released under the CC BY-SA license

1
When Have I Written
Enough Tests?
When Have I Written Enough Tests?
• When I’ve tested the valid inputs
• When I’ve tested all the code
• When the tests will catch bugs

3
Learning Goals for this Lesson
At the end of this lesson, you should be able to
• Explain how TypeScript types and documented
preconditions influence what tests you need to write
• Explain the difference between the any vs unknown
types in TypeScript
• Understand the structure of a simple Express server
incorporating Zod validation

4
What Inputs Should We Test?
What input values do I need to test this function on?
• Edge cases (definitely 0)
• Probably 1 and some larger number? But most numbers > 1 are kind of
interchangeable.
• What about -3? 1.4? NaN? null? { lol: 'owned' } ?
/** Returns an array that repeats "hello"
* @param numHellos - number of “hello”s to return, must be an integer >= 0
*/
function helloNTimes(numHellos: number): string[] {
const arr: string[] = [];
for (let i = numHellos; i !== 0; i--) { [Link]("hello"); }
return arr;
} 5
For Unit Testing, Tests Inputs Should
Respect a Function’s Contracts
• Unit Tests: testing a single function in isolation
• Unit testing only needs to give a function tests that respect the functions
preconditions: no need to test -3 or 1.4
• Integration Tests test how parts of a program work together: that’s
where we ensure other functions respect our function’s contracts.
/** Returns an array that repeats "hello"
* @param numHellos - number of “hello”s to return, must be an integer >= 0
*/
function helloNTimes(numHellos: number): string[] {
const arr: string[] = [];
for (let i = numHellos; i !== 0; i--) { [Link]("hello"); }
return arr;
} 6
What Trusting Contracts Looks Like
/**
* Adds a message to a chat, updating the chat
*
* @param chatId - Ostensible chat id
* @param user - Authenticated user
* @param messageId - Valid message id
* @returns the updated chat info object
* @throws if the chat id is not valid
*/
export function addMessageToChat(
chatId: string,
user: UserWithId,
messageId: string
): ChatInfo {

7
TypeScript Types Help With (Some) Contracts
• TypeScript is helpful, but it’s obviously only providing help
in making sure that the function gets 3 and not “three”.
It’s not going to help with 3 versus 3.1 or -8 or NaN.

/** Returns an array that repeats "hello"


* @param numHellos - number of “hello”s to return, must be an integer >= 0
*/
function helloNTimes(numHellos: number): string[] {
const arr: string[] = [];
for (let i = numHellos; i !== 0; i--) { [Link]("hello"); }
return arr;
} 8
TypeScript Types Are Easily Circumvented (1)
• In a language like Java, we’d need to worry that another
function could call helloNTimes with -3: calling the
function with a string or null is a compiler error.
• That’s not true in TypeScript, and that can be surprising.

/** Returns an array that repeats "hello"


* @param numHellos - number of “hello”s to return, must be an integer >= 0
*/
function helloNTimes(numHellos: number): string[] {
const arr: string[] = [];
for (let i = numHellos; i !== 0; i--) { [Link]("hello"); }
return arr;
} 9
TypeScript Types Are Easily Circumvented (2)
• In a language like Java, we’d need to worry that another
function could call helloNTimes with -3: calling the
function with a string or null is a compiler error.
• That’s not true in TypeScript, and that can be surprising.
• TypeScript will happily accept the following as a well-
typed expression:
helloNTimes({ lol: 'owned ' } as unknown as number)

• They do seem to make it less likely you’ll screw up


accidentally…, and ESLint + TypeScript work together to do
even more 10
Where Might An Untrusted Input Come
From?
Any input given to a web app
can also be given by other
means…

curl [Link] -H 'Content-Type: application/json' \


--data '{ "username": "trugamer", "password": "Hunter2" }'
11
Untrusted Inputs Should Have Type unknown
• The appropriate TypeScript type for an unknown value is unknown

function lookAtMe(input: unknown) { TypeScript error here!


[Link]([Link]());
if (typeof input === "string") {
[Link]([Link]());
} it’s ok here!
}

• If you use the any type instead, TypeScript will just say “ok, I guess
you know what you’re doing”
12
How Can unknown Inputs Be Used Safely?
This can get complicated fast…

export type Auth = { username: string, password: string }

function useAuth(x: unknown) {

if (
(typeof x === 'object' && x !== null) &&
('username' in x && typeof [Link] === 'string’) &&
('password' in x && typeof [Link] === 'string’)
) {
const auth: Auth = { username: [Link], password: [Link] };
// write the code you care about here!
} 13
}
Libraries Make Checking Types Easier
Zod is a library that makes checking structure less tedious & error-prone.
import { z } from 'zod’;
const zAuth = [Link]({ username: [Link](), password: [Link]() });
export type Auth = [Link]<typeof zAuth>;

function useAuth(x: unknown) {


const parseResult = [Link](x);
if ([Link]) {

const auth: Auth = [Link];


// write the code you care about here!
} 14
}
Some Libraries Use any: Common But
Dangerous
import express from 'express';
const app = express();
[Link]([Link]());

type Auth = { username: string; password: string };


[Link]('/', (req, res) => { This has type “any”
const auth: Auth = [Link];

if ([Link] !== 'secret') {


[Link](403).send({ error: 'Wrong password' });
} else {
[Link]({ message: `WELCOME,${[Link]()}` });
}
});
[Link](8000, () => [Link](`Listening on port 8000`)); 15
Improving This Web Server With Zod
import { z } from 'zod';
import express from 'express';
const app = express();
[Link]([Link]());

const zAuth = [Link]({ username: [Link](); password: [Link]() });


[Link]('/', (req, res) => {
const auth = [Link]([Link]);
if ([Link]) {
[Link](400).send({ error: 'Unexpected message' });
} else if ([Link] !== 'secret') {
[Link](403).send({ error: 'Wrong password' });
} else {
[Link]({ message: `WELCOME,${[Link]()}` });
}
});
[Link](8000, () => [Link](`Listening on port 8000`)); 17
Zod Can Check Conditions Even Finer than
TypeScript Types

const zHelloInput = [Link]().gte(0);

/** Returns an array that repeats "hello"


* @param numHellos - number of times to say “hello”
* @throws if the input is not an integer >= 0
*/
function helloNTimes(numHellos: unknown): string[] {
const parseResult = [Link](numHellos);
if (![Link]) throw new Error("Invalid input");
const arr: string[] = [];
for (let i = [Link]; i !== 0; i--) { [Link]("hello"); }
return arr;
}
18
Review
• One view of TypeScript is that it’s a handy way of
documenting, and imperfectly checking, the
contracts (preconditions and postconditions) of
your code
• Do you need to test inputs that violate your
contracts? It depends!
• You can never trust that the input to a web server
will obey any sort of contract — important to test!

19

You might also like