KEMBAR78
Communicating Intention with Functional TypeScript | PPTX
Communicating Intention
with Functional TypeScript
What don’t you like
about TypeScript?
λ
+
!101
Thiago Temple
Software Engineer
@SurveyMonkey
Function
Signatures
The Any
Type
Strings
Invalid
Object
States
Immutability
What is This Function
Doing?
Function Signatures
function add(n1: number, n2: number) { }
function processOrder(order: Order, options: Orde
type OrderResult =
ProcessedOrder |
OrderAction |
string;
function processOrder
( order: Order,
options: OrderOptions): OrderResult {
type OrderResult =
ProcessedOrder |
OrderAction |
string;
type OrderProcessingError = string;
type OrderResult =
ProcessedOrder |
OrderAction |
string;
type OrderProcessingError = string;
type OrderResult =
ProcessedOrder |
OrderAction |
OrderProcessingError;
Error Handling
function processOrder
( order: Order,
options: OrderOptions) {
Avoiding Throwing Exceptions
type Result<TResult, TError> =
Success<TResult> |
Failure<TError>;
type Success<T> = {
kind: "successfulResult";
value: T;
};
type Failure<T> = {
kind: "failedResult";
error: T;
};
type OrderResult =
Result<ProcessedOrder | OrderAction,
OrderProcessingError>;
function processOrder(
order: Order,
options: OrderOptions): OrderResult {
type OrderResult =
Result<ProcessedOrder | OrderAction,
OrderProcessingError>;
function processOrder(
order: Order,
options: OrderOptions): OrderResult {
type OrderResult =
Result<ProcessedOrder | OrderAction,
OrderProcessingError>;
function processOrder(
order: Order,
options: OrderOptions): OrderResult {
const result =
processOrder(order, orderOptions);
if (result.kind === "failedResult") {
result.error;
// OrderProcessingError A.K.A. string
} else {
result.value;
// ProcessedOrder | OrderAction
}
null
• Not Found
• An Error Has Occurred
• Unpredicted Case
• Something is Not Required
• Condition as False-y
• And More…
Versatile
Don’t Use null
function findUserById(id: string):
User | null {
type Option<T> =
Some<T> | None;
type Some<T> = {
kind: "someOption";
value: T;
};
type None = {
kind: "noneOption";
}
type Option<T> =
Some<T> | None;
function findUserById(id: string):
Option<User> {
const user =
findUserById(userId);
if (user.kind === "someOption") {
user.value; // User
}
Any What?
–Thiago Temple
“Don’t use any as a catch-all for types you’re unsure
of, also, don’t be lazy.”
Valid Uses of any
• When you know nothing about the given value
• When you don’t care about the given value
unknown
function exec(args: unknown) {
args["someKey"] // Compile Error
}
function exec<T extends { someKey: string }>(args: T) {
args["someKey"] // OK
}
const val1 = lib.someFun();
lib2.otherFunc(val1);
// Err: val2 is of != type
const val1: any = lib.someFun();
lib2.otherFunc(val1);
const val1 = lib.someFun();
lib2.otherFunc(val1 as any);
L
I
M
I
T
I
N
G
S
T
R
I
N
G
S
function findByGenre(genre: string): Movie[]
type Genre =
"Drama" |
"Comedy" |
"Action";
function findByGenre(genre: Genre): Movie[] {
type OrderStatus =
"New" |
"Paid" |
"Shipped" |
"Completed";
type Order = {
status: OrderStatus
// ...
};
function processOrder(order: Order): Order {
switch(order.status) {
case "New":
return ...;
case "Paid":
return ...;
case "Shipped":
return ...;
case "Completed":
return ...;
}
}
function processOrder(order: Order): Order {
switch(order.status) {
case "New":
return ...;
case "Paid":
return ...;
case "Shipped":
return ...;
case "Completed":
return ...;
}
}
nvalid States Impossible to Re
type Payment = {
amount: number;
creditCardInfo?: CreditCardInfo;
payPalInfo?: PayPalInfo;
cash: boolean;
}
const payment: Payment = {
amount: 42,
cash: false
};
type Payment = {
amount: number;
paymentInfo: PaymentInfo;
}
type PaymentInfo =
CreditCardInfo |
PayPalInfo |
Cash;
type CreditCardInfo = {
kind: "creditCardInfo";
// ...
};
type PayPalInfo = {
kind: "payPalInfo";
// ...
};
type Cash = {
kind: "cash";
}
const payment: Payment = {
amount: 42,
paymentInfo: {
kind: "creditCardInfo",
number: 12321312312312312312312
}
};
type Order = {
orderId: string;
billingAddress: Address;
shippingAddress?: Address;
};
type Order = {
orderId: string;
billingAddress: Address;
shipping: ShippingInfo;
};
type ShippingInfo =
SameAsBilling |
DownloadableContent |
ShippingAddress;
type SameAsBilling = {
kind: "sameAsBilling";
}
type DownloadableContent = {
kind: "downloadableContent";
}
type ShippingAddress = {
kind: "shippingAddress";
address: Address;
}
ShippingResult {
switch(order.shipping.kind) {
case "downloadableContent":
return ...;
case "sameAsBilling":
return ...;
case "shippingAddress":
order.shipping.address; // Address
ShippingResult {
switch(order.shipping.kind) {
case "downloadableContent":
return ...;
case "sameAsBilling":
return ...;
case "shippingAddress":
order.shipping.address; // Address
ShippingResult {
switch(order.shipping.kind) {
case "downloadableContent":
return ...;
case "sameAsBilling":
return ...;
case "shippingAddress":
order.shipping.address; // Address
ShippingResult {
switch(order.shipping.kind) {
case "downloadableContent":
return ...;
case "sameAsBilling":
return ...;
case "shippingAddress":
order.shipping.address; // Address
ShippingResult {
switch(order.shipping.kind) {
case "downloadableContent":
return ...;
case "sameAsBilling":
return ...;
case "shippingAddress":
order.shipping.address; // Address
What Happened With my Data
function processOrders(orders: readonly Order[]) {
orders.push({...}) // Error
orders.pop() // Error
orders.reverse() // Error
}
function processOrders(order: readonly Order) {
function processOrders
(order: Readonly<Order>) {
order.orderId = 123; // Error
order.customer.customerId = 123; // OK
}
type Immutable<T> = {
readonly [K in keyof T]: Immutable<T[K]>;
}
type Order = Immutable<{
orderId: string;
customer: Customer;
billingAddress: Address;
shipping: ShippingInfo;
}>;
function processOrder(order: Immutable<Order>)
function processOrder(order: Immutable<Order>) {
order.orderId = ""; // Error
order.customer.customerId = ""; // Error
order.shipping = { kind: "sameAsBilling" }; // Error
}
Final Thoughts and
Recap
Compiled Code Doesn’t Get Stale
Think About The Next Person
(and yourself)
nest About Your Function Sign
Be Mindful With Your Use of any
Limit the Scope of Strings
nvalid States Impossible to Re
surveymonkey.com/careers
Thank You
@ThiagoTemple

Communicating Intention with Functional TypeScript