TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.
Compiler options
Strict mode
"The strict flag enables a wide range of type checking behavior that results in stronger guarantees of program correctness."
Strict mode
- strictNullChecks
- strictBindCallApply
- strictFunctionTypes
- strictPropertyInitialization
- noImplicitAny
- noImplicitThis
- useUnknownInCatchVariables
- ...
strictNullChecks
- Enforce edge case implementation
- Make assumptions about optionals visible
- Easy to catch optionals during PR review
// Types for array.find
interface Array<T> {
find(...): T | undefined;
}
// Example
const item2 = collection
.find(item => item.id === '2');
console.log(item2.name);
// More robust code
if(item2 !== undefined) {
console.log(item2.name);
} else {
// Implement edge case
}
// More transparant code
console.log(item2!.name);
noImplicitAny
- Use TS to full potential
- Make code refactorable
interface Person {
id: string;
name: string;
age?: number;
}
function logName(person) {
console.log(person.name);
}
function logName(person: Person) {
console.log(person.name);
}
interface Identity {
name: string;
}
interface Person extends Identity {
id: string;
age?: number;
}
function logName(identity: Identity) {
console.log(identity.name);
}
Coupled
Built-in utilities
// Decoupled from Person
function logName(identity: Record<"name", string>) {
console.log(identity.name);
}
// Equivalent to Record
function logName(identity: { name: string }) {
console.log(identity.name);
}
Record
Record
interface Person {
id: string;
name: string;
age?: number;
}
let incomplete: Partial<Person> = {};
incomplete = { id: '1' };
const complete: Required<Person> = {
id: '1',
name: 'John Doe',
age: 64
};
Partial/Required
interface Person {
id: string;
name: string;
age?: number;
}
// Whitelist
const personWithOnlyAge: Pick<Person, "age"> = {
age: 60
};
// Blacklist
const personWithoutAge: Omit<Person, "age"> = {
id: '1',
name: 'John Doe'
};
const completePerson: Person = {
...personWithOnlyAge,
...personWithoutAge
};
Pick/Omit
// Omit before it was added to TS
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
// Example of filtering object values on type
type FilterFlags<Base, Type> = {
[Key in keyof Base]: Base[Key] extends Type ? Key : never;
};
type AllowedNames<Base, Type> = FilterFlags<Base, Type>[keyof Base];
export type SubType<Base, Type> = Pick<Base, AllowedNames<Base, Type>>;
const personStrings: SubType<Person, string> = {
id: '1',
name: 'John Doe'
};
// Example of handling nested structures
export type RecursivePartial<T> = {
[Key in keyof T]?: T[Key] extends (infer U)[]
? RecursivePartial<U>[]
: T[Key] extends object
? RecursivePartial<T[Key]>
: T[Key];
};
Advanced
String literal
interface Order {
status: string;
}
enum OrderStatus {
...
}
interface Order {
// Coupled to enum
status: OrderStatus;
}
interface Order {
// Typed with string literal
status: "open" | "closed";
}
Magic strings
interface OpenOrder {
status: 'open';
deadline: Date;
}
interface ClosedOrder {
status: 'closed';
closedBy: Employee;
}
type Order = OpenOrder | ClosedOrder;
const order: Order = {...};
if(order.status === 'open') {
order.
} else {
order.
}
String literal
Generics<T>
// Decoupled from Person
interface Identity {
name: string;
}
function logName<T extends Identity>(identity: T) {
console.log(identity.name);
}
extends
Data normalization
interface Person {
id: string;
name: string;
age?: number;
}
const people: Person[] = [{...}, {...}, {...}];
// O(n)
people.find(({id}) => id === '1');
const normalizedPeople = {
byId: {
'1': {...},
'2': {...},
'3': {...}
},
allIds: ['1', '2', '3']
};
// O(1)
normalizedPeople.byId['1'];
Data normalization
type Identity = { id: string };
interface Normalized<T extends Identity> {
byId: Record<string, T>;
allIds: string[];
}
function normalize<T extends Identity>(collection: T[]): Normalized<T> {
return collection.reduce(
(acc, value) => ({
byId: {
...acc.byId,
[value.id]: value,
},
allIds: [...acc.allIds, value.id],
}),
{
byId: {},
allIds: [],
} as Normalized<T>
);
}
const people: Person[] = [...];
const peopleNormalized = normalize(people);
// O(1)
peopleNormalized.byId['1'];
Data normalization
function toCollection<T extends Identity>(normalized: Normalized<T>): T[] {
return normalized.allIds.map(id => normalized.byId[id]);
}
const peopleNormalized: Normalized<Person> = {...};
const people = toCollection(peopleNormalized);
Questions?
TypeScript
By rachnerd
TypeScript
- 164