tags : Web Development, Javascript, Programming Languages

Intro stuff

  • JS is weakly-typed. So type-coercion can happen
    • Primitive coercion: [@@toPrimitive]("default") → valueOf() → toString()
    • Numeric coercion: [@@toPrimitive]("number") → valueOf() → toString()
    • String coercion: [@@toPrimitive]("string") → toString() → valueOf()
  • TS is a programming language developed by Microsoft.
    • Typed superset of Javascript
    • Includes its own compiler.

Self realizations (aha moments)

  • TS lets you play with types the way you play with values in a general programming language. Eg. You have a type and now you want to extract/transform parts of it into another type.
  • This kind of thing you usually do with values but with typescript this is now possible. It’s sort of has a regex feel to types, in-sense what regex makes possible.
  • Type functions and Values
    • type ReturnWhatIPassIn<T> = T; : This right here is a function that handles types, unlike functions that handle values that I know of in normal Javascript syntax.
    • type ReturnWhatIPassIn = string; : This right here is a static value of a type.
    • So you can basically play with types just the way you play with values
  • Just like programming with values, there are multiple ways of getting the same result. Getting better at it will take time and more doing w it.

Primitive types

  • All basic types of Javascript
  • any : allow anything
  • unknown : ensure someone using this type declares what the type is
  • never: it’s not possible that this type could happen
  • void: a function which returns undefined or has no return value

Custom types

  • interface
    • Mutable
    • Good for behavior, can be extended and merged
    • Can be used for a subset of things, functions, objects etc.
  • type
    • Immutable
    • More features than interface
    • Good for data, can be intersected(composed using | and &).
    • Can be used for everything.

To use types or interfaces ?

  • Use interface until you need to use features from type. This goes the opposite way too.
  • Doesn’t really matter which one you go with.
  • But some people use type by default too. It boils down to personal preferences and needs.
  • My current verdict as of June’23: I’ll go w default type, if I need something that needs extending, use interface

What about namespace?

Composing types

union

  • Basically using |
  • You can’t really declare them, conceptually helpful to think of them like possibilities(multiverse).
  • Only types can be union’ed not interfaces.
  • We also have discriminated unions, where there’s a common property that distinguishes what’s getting union’ed and then we can later use typeof

generics

  • Every function call has 2 argument lists
    • Value arguments(runtime) : Parenthesis ()
    • Type arguments(typecheck/compile time) : Pointy brackets <>
  • We can think generics as the type arguments, sort of fill-in-the-blanks types.
  • It allows your type to more dynamic by allowing other types to be part of the original type. Eg. You couldn’t indicate the type of array at all without generics. Array<string> is using generics.
  • Generics in classes and functions are different but use similar syntaxes.
  • We do want to constraint the parameter into a generic if we’re doing things specific to the certain type. Usually constraint to generic parameter is added with extends. Eg. Poop<T extends string>
  • For reverse constraint, you can use Object or {} to match anything but null or undefined

Duck type

If it walks like a duck and it quacks like a duck, then it must be a duck.

TS follows duck typing. If the structure is same, you need to explicitly set the type.

interface Point {x: number; y: number;}
function logPoint(p: Point) {console.log(`${p.x}, ${p.y}`);}
const point = { x: 12, y: 26 };
const point2 = { x: 12, y: 26, z: 89 }; // this would work too! needs to be a subset
class VirtualPoint {
  x: number;
  y: number;
  constructor(x: number, y: number) {this.x = x; this.y = y;}
}
const point3 = new VirtualPoint(13, 56);
logPoint(point); // works, but point was never mentioned to be "Point"
logPoint(point2); // works, but point was never mentioned to be "Point"
logPoint(point3); // works, but point was never mentioned to be "Point"

Random things

Levels of TS

  • Values exist at the dynamic level.
  • Types exist at the static level.

Similarly,

  • Normal functions exist at the dynamic level, factories for values
    const valueFactory = (x: number) => x; // definition
    const myValue = valueFactory(123); // use
  • Generic types exist at the static level, factories for types
    type TypeFactory<X> = X; // definition
    type MyType = TypeFactory<string>; // use
  • Working together
    function identity<Arg>(arg: Arg): Arg {
      return arg;
    }
    // %inferred-type: number
    const num1 = identity<number>(123);
    // %inferred-type: 123 NOTE: It's 123 and not number
    const num2 = identity(123);

import type

See the following

Do I need to use the import type

Index signatures

  • TypeScript index signatures must be either string or number (also symbols?)
interface Cache {
  [id:string] : string;
}

Type Definitions from where?

To be able to show errors and hints from other packages, the compiler relies on declaration files. Two sources,

  • Bundle: check if a library has bundled types, look for an index.d.ts file in the project.
  • DefinitelyTyped: Crowdsourced repo
  • Local Declarations: Define it in declarations.d.ts maybe

declare keyword

  • declare specifies a type to an already existing variable, not declaring a new one.
  • eg. get external js file which has foo identifier, tsc doesn’t know about it. So we do declare const foo : string, to tell the compiler that don’t worry about foo, this is what it looks like it already has a type.

Asserting type

  • When you know better exactly what type it would be.
  • Only allows type assertions which convert to a more specific or less specific version of a type. (impossible shit not allowed)
    const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;
    const a = (expr as any) as T; // workaround if ts is too strict
  • non-null assertions using ! in cases where we trying to do something in an optional(?) property.

Modules in TS

  • Any file containing a top-level import or export is considered a module and is isolated.
  • Any file without a top-level import or export is considered as a script whose contents are available in the global scope

Export

  • export declarations
  • export statements
  • re-exports

Import

  • importing other modules
  • explicit importing of types (guaranteed to be removed from your JavaScript, and tools like Babel can make better assumptions about your code)

Tooling

  • The compiler (tsc), way to use it is via npx tsc
  • tsconfig bases: https://github.com/tsconfig/bases
    • I like the base/tsconfig repo, but only for CJS configurations. Some mistakes might be there for esm. verify.
    • using “moduleResolution”: “node”, but using “node” as the value is telling TypeScript to emit JavaScript that resolves as a CommonJS module.
  • what is @types??

Declaration files

  • .d.ts

Resources