Learn Typescript by writing Javascript

How I'd approach learning Typescript if I had to start from scratch

TL;DR: while learning its syntax, instead of trying to write Typescript, you can write Javascript and let the compiler and your IDE do the work for you.

Hover over Javascript functions & variables to see their TS definitions

If you want to type an existing piece function or value, you can use typeof VARIABLE_NAME and Typescript's compiler will introspect the type from your Javascript, without you having to manually type it out.

Besides lowering the barrier to entry, this also speeds things up quite significantly. And you still get TS's amazing auto-complete and warnings!

Perhaps more importantly, if you use TS-friendly IDEs like VS Code you can hover over a variable definition to get the shape of the data and start learning its syntax.

Use these tricks with a basic knowledge of Type Aliases, and you've pretty much got to 80% of Typescript values.

Of course, there are unions, assertions, narrowing, type casting, constraints, overloads and-otherso-confusing-my-brain-is-melting features 🫠

These are mainly for library authors, though.

I've been using TS for almost 5 years and I still write mostly type definitions (or interface, basically the same but for objects) and enums (fancy key/value objects). Here's an adaption of real-world code I wrote recently:

// We often use library-provided types
import { SanityDocument } from '@sanity/client'

type TranslationInMeta = {
  /**
   * Using TSDoc comments is great, by the way!
   * @link https://tsdoc.org/
   */
  _key: string
  _ref: string
}

// Sometimes we do fancy merging of types...
type TranslationMetaDoc = TranslationInMeta & SanityDocument {
  locales: TranslationInMeta[]
}

export function useDocumentTranslations({
  currentDocument,
}: {
  currentDocument?: SanityDocument
}) {
  // It's okay to use `any`, too!
  const routerContext = React.useContext<any>(RouterContext)

  const [translationsDocument, setTranslationsDocument] =
    React.useState<TranslationMetaDoc | null>(null)

  // ...
}

Looks mostly like React Javascript, right? That's because it is! Just a "flavored" version of it 😉

Now, if you're a library author, you may find yourself writing code like this... (source)

export function createMachine<
  TContext,
  TEvent extends EventObject = AnyEventObject,
  TTypestate extends Typestate<TContext> = { value: any; context: TContext },
  TServiceMap extends ServiceMap = ServiceMap,
  TTypesMeta extends TypegenConstraint = TypegenDisabled
>(
  config: MachineConfig<
    TContext,
    any,
    TEvent,
    BaseActionObject,
    TServiceMap,
    TTypesMeta
  >,
  options?: InternalMachineOptions<
    TContext,
    TEvent,
    ResolveTypegenMeta<TTypesMeta, TEvent, BaseActionObject, TServiceMap>
  >
): StateMachine<
  TContext,
  any,
  TEvent,
  TTypestate,
  BaseActionObject,
  TServiceMap,
  ResolveTypegenMeta<TTypesMeta, TEvent, BaseActionObject, TServiceMap>
>;

I honestly don't even know how this code works - it's a function that has no body, and there's another declaration of a function with the same name right below it. Too much for my brain to handle 😵‍💫

So yeah, take it easy, start by writing Javascript and adopt TS incrementally :)


Credits to Clara Amenyo for sparking this idea in a pairing session during my Recurse Center batch :)