Skip to content

Instantly share code, notes, and snippets.

@jahe
Last active June 15, 2021 23:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save jahe/62d4389850c75f3ff393ce6665742de2 to your computer and use it in GitHub Desktop.
Save jahe/62d4389850c75f3ff393ce6665742de2 to your computer and use it in GitHub Desktop.
TypeScript Cheatsheet
// Activate the family of strict options
// You can then selectively opt out with specific strict options set to false
--strict
// Properties in ES6 classes should be initialized
//
// It checks weather one of the following conditions evaluatest to true:
// * has a type that includes undefined
// * has an explicit initializer
// * is definitely assigned to in the constructor
//
// The --strictNullChecks flag must be set in order for this option to have any effect
--strictPropertyInitialization
// Definite Assignment Operator
// Use the "!" operator on class properties to indicate that we know that a property eventually gets initialized at runtime
// so that no explicit initialization is required (e.g. when using a dependency injection framework)
class Library {
titles!: string[]
}
// Use the JS "in" operator to infer one of two possible types (type guard)
function redirect(user: Admin | User) {
if ("role" in user) {
routeToAdminPage(user.role)
} else {
routeToHomePage(user.email)
}
}
// Use Descriminative Unions to distinguish between types in a switch statement based on an objects property value
class RemoveAllAction {
readonly type = "Remove All"
}
class RemoveOneAction {
readonly type = "Remove One"
constructor(public payload: number) {}
}
type Actions = RemoveAllAction | RemoveOneAction
function todoReducer(
action: Actions,
state
) {
switch (action.type) {
case "Remove All": {
// action is infered to be of type RemoveAllAction
}
case "Remove One": {
// action is infered to be of type RemoveOneAction
}
}
}
// Force a switch statement to include all possible cases with a default block with a variable of type never
switch (action.type) {
case "Remove All": {
// ...
}
default: {
const x: never = action // <- This causes a compile error since "Remove One" is not part of the switch statement
}
}
// Use the Mapped Type Modifies to make e.g. a readonly version of an interface
// by adding or removing type modifies (with "+" or "-" whereas "+" is optional)
interface IPet {
name: string
age: number
favoritePark?: string
}
type ReadonlyPet = {
readonly [K in keyof IPet]: IPet[K]
}
type ExplicitPlusReadonlyPet = {
+readonly [K in keyof IPet]: IPet[K]
}
type StringPet {
[K in keyof IPet]: IPet[K] | string
}
type NonOptionalPet {
[K in keyof IPet]-?: IPet[K]
}
// Types (or "Type Aliases") vs. Interfaces
// - Library authors should use Interfaces to let users extend them locally
// - Interfaces with the same name are getting merged - Type Aliases not
// - Type Aliases are better for aliasing a specifc function
// - Union Type Aliases can't be used for class or interface inheritance since they can be either one type or the other
// Extend/Change an Interface of a library locally
// TS merges the declaration of Interfaces with the same name
// Add typings.d.ts file in the same directory
interface JQuery {
hideChildren(): JQuery
}
// Use the "unknown" type e.g. for server responses which can do change
// You can't access anything on an "unknown" type
// For accessing it you have to apply flow type narrowing or casting to it
// to make sure that it has a specific structure and type
interface IDataService {
getData(): unknown
}
const service: IDataService
const response = service.getData()
response.toUpperCase() // -> Throws a compilation error
if (typeof response === 'string') {
response.toUpperCase() // -> Works (after making the check)
}
const castResponse = <string> response
castResponse.toUpperCase() // -> Works (after making the casting)
// User Defined Type Guard
function isComment(type: any): type is IComment {
return (<IComment> type).message !== undefined
}
// Object with enum values as keys
// Source: https://stackoverflow.com/questions/52700659/how-to-use-enum-as-index-key-type-in-typescript
enum Letter {
A = 'a',
B = 'b'
}
interface LetterToString {
[key in Letter]?: string
}
// Work with "strict" mode on
// Filter null values from an array
const myFoos = myFoosWithNull.filter((foo): foo is Foo => foo != null)
// OR:
function isPresent<T>(value: T): value is NonNullable<T> {
return value != null
}
const myFoos = myFoosWithNull.filter(isPresent)
// Prevent never[] error messages when using empty arrays with "as ..." keyword
return {
names: [] as string[]
}
// Define types for a npm package that has no TypeScript types
// For example 'markdown-table' - It exports a function by default that expects a 2-dimensional array as an argument and returns a Markdown representation as string
// 1. Create a folder for the types of that npm package: src/@types/markdown-table
// 2. Create a TypeScript declaration file named index.d.ts in that folder with the following content:
declare function markdownTable(rows: any[][]): string
declare namespace markdownTable {}
declare module 'markdown-table' {
export = markdownTable
}
// If you need Node.js types just add the following comment to the top of the declaration file
/// <reference types="node" />
// Useful type for dictionary kind of objects where properties should be immutable
type Dictionary<T> = Partial<{ [key: string]: Readonly<T> }>
// Make some types globally available like the Dictionary
global.d.ts
// Enrich the window object with custom properties
// global.d.ts
interface Window {
customLink: string;
}
// Specify key and value of objects with Record<K, T>
const colorMap: Record<string, { color: string; hover: string }> = {
nextjs: { color: '#0A7B83E2', hover: '#09686dE2' },
javascript: { color: '#F5B50FE2', hover: '#d69e0cE2' }
}
// Omit some properties
type Person = {
name: strin
age: number
salary: number
}
type PersonWithoutSalary = Omit<Person, 'salary', 'age'>
const personObj: PersonWithoutSalary = {
name: 'Foo'
}
// Pick some properties
type Person = {
name: string
age: number
salary: number
}
type PersonWithoutSalary = Pick<Person, 'salary'>
const personObj: PersonWithoutSalary = {
salary: 10000,
age: 19 // Type '{ age: number }' is not assignable to type 'Pick<Person, "salary">'
}
// Create a type based on an existing object
const Person = {
name: 'John',
age: '20'
}
type PersonType = typeof Person
const newPersonObj: PersonType = {
name: 'Smith',
age: '21'
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment