Skip to content

Instantly share code, notes, and snippets.

@jviall
Last active January 23, 2023 20:17
Show Gist options
  • Save jviall/cef9fd511bd918e73602d4c8206db1b8 to your computer and use it in GitHub Desktop.
Save jviall/cef9fd511bd918e73602d4c8206db1b8 to your computer and use it in GitHub Desktop.
Scope -- TypeScript Advanced Techniques -- TS Playground Pres

Advanced TypeScript Techniques

Team Disco

  • Dylan Roberts
  • James Viall

Topic: Generics

Types that take parameters

Typing the useState React hook

// A contrived example that isn't really useful // since we can only use strings function useStringState(initialState: string): [getter: string, setter: (newValue: string) => void] { let state = initialState; const setter = (newValue: string) => { state = newValue; } return [state, setter]; } const [myString, setMyString] = useStringState("default");

// error, only accepts strings :/ setMyString(10);

// we can use a Generic Type Parameter to get type safety function useState< T>(initialState: T): [getter: T, setter: (newValue: T) => void] { let state = initialState; const setter = (newValue: T) => { state = newValue; } return [state, setter]; }

// return type is inferred by parameter type const [myObj, setMyObj] = useState({ value: 'default'}); // ^?

setMyString(10); // error setMyObj('object?') // error setMyObj({ MY_VALUE: 'value' }) // object, but not of same type: error

setMyObj({ value: "value" }) // correct

// Scope Codebase Example // useGridSortState (src/utilts/hooks.tsx:L464)


The keyof Operator

Given some object with keys, we can create a type out of the keys

// Topic: `keyof` operator // type Point = { x: number; y: number }; type P = keyof Point; // ^?

let Peas: P[];

// Scope Codebase Example // useRoutingRedirect (src/utils/hooks.tsx:L1660)


The typeof operator

// Javascript has this natively for run-time expression primitive types. // Prints "string" console.log(typeof "Hello world"); console.log(typeof (() => null));

// Typscript adds the ability to use this in a pure type context const s = "hello"; let n: typeof s; // ^?

// A more useful example // Comboing keyof with typeof const TOOLTIP_DELAY_MAP = { long: 500, none: 0, short: 350 };

interface ITooltipProps { delay?: keyof typeof TOOLTIP_DELAY_MAP; // ^? }

const Tooltip = ({ delay = "long" }: ITooltipProps): void => { console.log(delay); }; Tooltip({}); Tooltip({ delay: 'short'});

// Codebase Example // useRoutingRedirect (src/utils/hooks.tsx:L1660)


Topic: Indexed Access Types

Look up a specific property on another type

type Person = { age: number; name: string; alive: boolean }; type Age = Person["age"]; // ^? type I1 = Person["age" | "name"]; // ^?

type I2 = Person[keyof Person]; // ^?

type AliveOrName = "alive" | "name"; type I3 = Person[AliveOrName]; // ^?

// EXAMPLE IN CODEBASE // Element<T extends Array> in types/grids.ts “If I were to index into an Array, what type would its elements be?” // GraphQl query data types in src/utils/hooks:L707 getParentIdAndNameFromQuery()


Topic: Conditional Types

Types which act like if statements in the type system

interface Animal { live(): void; } interface Dog extends Animal { woof(): void; } type Example1 = Dog extends Animal ? number : string; // ^?

type Example2 = RegExp extends Animal ? number : string; // ^?

// EXAMPLE IN CODEBASE // IEntityKey<T extends IEntityType> src/types/entities.ts:L25


Topic: Mapped Types

Build on Index Signatures to clear types based on other types

// Often used with `keyof` operator type OptionsFlags< Type> = { [Property in keyof Type]: boolean; }; type FeatureFlags = { darkMode: () => void; newUserProfile: () => void; };

type FeatureOptions = OptionsFlags< FeatureFlags>; // ^?

// EXAMPLE IN CODEBASE // RecursivePartial<T> in src/types/ctvo.ts:L246


Topic: Template Literal Types

Mapped types which change properties via template string literals

type World = "world";

type Greeting = hello ${World}; // ^? type EmailLocaleIDs = "welcome_email" | "email_heading"; type FooterLocaleIDs = "footer_title" | "footer_sendoff";

type AllLocaleIDs = ${EmailLocaleIDs | FooterLocaleIDs}_id; // ^? type Lang = "en" | "ja" | "pt";

type LocaleMessageIDs = ${Lang}_${AllLocaleIDs}; // ^?

// EXAMPLE IN CODEBASE // Entity String Types in src/types/entitites.ts


Further Readings


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment