TypeScript Notes

đź“– tl;dr: Dump of Notes for TypeScript


  • Your existing working JavaScript code is also TypeScript code.

  • TypeScript knows the JavaScript language and will generate/infer types for you in many cases.

    let helloWorld = "Hello World";
  • You can explicitly describe this object’s shape using an interface declaration:

      interface User {
          name: string;
          id: number;
      }
  • You should prefer interface. Use type when you need specific features.

  • Generics provide variables to type:

      type StringArray = Array<string>;
      type NumberArray = Array<number>;
      type ObjectWithNameArray = Array<{ name: string }>;
  • Sample Generics

  • One of TypeScript’s core principles is that type checking focuses on the shape that values have. This is sometimes called duck typing or structural typing.

  • There is no difference between how classes and objects conform to shapes:

      class VirtualPoint {
          x: number;
          y: number;
    
          constructor(x: number, y: number) {
              this.x = x;
              this.y = y;
          }
      }
    
      const newVPoint = new VirtualPoint(13, 56);
      logPoint(newVPoint); // logs "13, 56"
  • TypeScript act a bit more strictly. In that case, you can use the noEmitOnError compiler option.

  • That’s why TypeScript needs a compiler in the first place - it needs some way to strip out or transform any TypeScript-specific code so that you can run it.

  • Using any often defeats the purpose of using TypeScript in the first place. The more typed your program is, the more validation and tooling you’ll get.

  • Turning on the noImplicitAny flag will issue an error on any variables whose type is implicitly inferred as any.

  • The type names String, Number, and Boolean (starting with capital letters) are legal, but refer to some special built-in types that will very rarely appear in your code. Always use string, number, or boolean for types.

  • To specify the type of an array like [1, 2, 3], you can use the syntax number[].

  • Note that [number] is a different thing; refer to the section on Tuples.

  • When you don’t specify a type, and TypeScript can’t infer it from context, the compiler will typically default to any. Because any isn’t type-checked. Use the compiler flag noImplicitAny to flag any implicit any as an error.

  • TypeScript doesn’t use types on the left style declarations like int x = 0; Type annotations will always go after the thing being typed.

  •   const names = ["Alice", "Bob", "Eve"];
      
      // Contextual typing for function - parameter s inferred to have type string
      names.forEach(function (s) {
        console.log(s.toUpperCase());
      });
      
      // Contextual typing also applies to arrow functions
      names.forEach((s) => {
        console.log(s.toUpperCase());
      });

    Even though the parameter s didn’t have a type annotation, TypeScript used the types of the forEach function, along with the inferred type of the array, to determine the type s will have.

    This process is called contextual typing because the context that the function occurred within informs what type it should have.

  • Object types can also specify that some or all of their properties are optional. To do this, add a ? after the property name:

      function printName(obj: { first: string; last?: string }) {
        // ...
      }
      // Both OK
      printName({ first: "Bob" });
      printName({ first: "Alice", last: "Alisson" });
  • A union type is a type formed from two or more other types, representing values that may be any one of those types.

  • TypeScript will only allow an operation if it is valid for every member of the union. For example, if you have the union string | number, you can’t use methods that are only available on string:

    function printId(id: number | string) {
      console.log(id.toUpperCase());
      // Property 'toUpperCase' does not exist on type 'string | number'.
      // Property 'toUpperCase' does not exist on type 'number'.
    }
  • Type aliases and interfaces are very similar, and in many cases you can choose between them freely. Almost all features of an interface are available in type, the key distinction is that a type cannot be re-opened to add new properties vs an interface which is always extendable.

    // Extending an interface
    
    interface Animal {
      name: string;
    }
    
    interface Bear extends Animal {
      honey: boolean;
    }
    
    const bear = getBear();
    bear.name;
    bear.honey;
          
    // Extending a type via intersections
    type Animal = {
      name: string;
    }
    
    type Bear = Animal & { 
      honey: boolean;
    }
    
    const bear = getBear();
    bear.name;
    bear.honey;
  • You can use a type assertion to specify a more specific type:

    const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;
  • You can also use the angle-bracket syntax (except if the code is in a .tsx file), which is equivalent:

    const myCanvas = <HTMLCanvasElement>document.getElementById("main_canvas");
  • By themselves, literal types aren’t very valuable:

    let x: "hello" = "hello";
    // OK
    x = "hello";
    // ...
    x = "howdy";
    // Type '"howdy"' is not assignable to type '"hello"'.

    It’s not much use to have a variable that can only have one value! But by combining literals into unions, you can express a much more useful concept - for example, functions that only accept a certain set of known values:

    function printText(s: string, alignment: "left" | "right" | "center") {
    // ...
    }
    printText("Hello, world", "left");
    printText("G'day, mate", "centre");
    // Argument of type '"centre"' is not assignable to parameter of type '"left" | "right" | "center"'.
  • TypeScript also has a special syntax for removing null and undefined from a type without doing any explicit checking. Writing! after any expression is effectively a type assertion that the value isn’t null or undefined:

    function liveDangerously(x?: number | null) {
      // No error
      console.log(x!.toFixed());
    }
  • Arrays are object types in JavaScript.
  • But it turns out that in JavaScript, typeof null is actually "object"!

  • For methods, note that the parameter name is required. The function type (string) => void means a function with a parameter named string of type any!