DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 963,274 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for 🀯 Typescript features you may not know
Johan du Toit
Johan du Toit

Posted on • Updated on

🀯 Typescript features you may not know

TypeScript- the love child of C# and JavaScript which you should be proud of and show off to your family.

Topics

πŸ”‘ keyof
πŸ” Access Modifiers
⚑ static
πŸš” Type Guards (User-defined)
πŸ’” Partial
⛏ Pick & Omit

πŸ”‘ keyof

This one does what it says on the tin. It gets the keys of an interface, type or object.


interface MyType {
  id: number;
  category: number;
  name: string;
}

function getProperty<T>(item: T, key: keyof T) {
  // more code goes here
}

// const item: MyType = { ... }

getProperty(item, 'id') // Valid
getProperty(item, 'other') // Error

πŸ” Access Modifiers

Access modifiers allow us to close down (or open up) access to properties on a class.

private: Only accessible within the class it was defined in.
protected: Accessible within the class it was defined in & any classes inheriting from it.
public: Accessible from anywhere.

class BaseClass {
  private privateProperty: string;
  protected protectedProperty: string;
  public publicProperty: string; // default is public, can be omitted

  constructor(
    privateProperty: string,
    protectedProperty: string,
    publicProperty: string
  ) {
    this.privateProperty = privateProperty;
    this.protectedProperty = protectedProperty;
    this.publicProperty = publicProperty;
  }

  baseMethod() {
    console.log(this.privateProperty); // Valid
    console.log(this.protectedProperty); // Valid
    console.log(this.publicProperty); // Valid
  }
}

class ParentClass extends BaseClass {
  methodA() {
    console.log(this.privateProperty); // Invalid
    console.log(this.protectedProperty); // Valid
    console.log(this.publicProperty); // Valid
  }
}

let item = new ParentClass("private", "protected", "public");
console.log(item.privateProperty); // Invalid
console.log(item.protectedProperty); // Invalid
console.log(item.publicProperty); // Valid

Note JavaScript doesn't have the concept of access modifiers as we know them (yet!), so it's only enforced in TypeScript land. Once it hits JavaScript land it's public all the way.

⚑ Static

With static we can add properties to a class and access it without the need to create an instance of the class.

class MyClass {
  static value = 13.31;
  value = 89.98;

  process(num: number) {
    return num * MyClass.value;
  }
}

console.log(MyClass.value) // 13.31

// const myClassInstance = new MyClass();
// console.log(myClassInstance.value); // 89.98 (using the instance value and not the static value)

πŸš” Type Guards (User-defined)

Have you ever written a function that accepts a union type and then experienced trouble when trying to handle the different types, look no further! Type Guards allow us to define a function that will perform a runtime check that will assure you of a single type within some scope.

Some of you may think that typeof is the solution, but sadly this only works for primitive types.

type MyTypeA = {
  typeAProp: number;
  otherAProp: string;
  sameProp: boolean;
};

type MyTypeB = {
  typeBProp: number;
  otherBProp: string;
  sameProp: boolean;
};

function myFunc(item: MyTypeA | MyTypeB) {
  if (isMyTypeA(item)) {
    console.log(item.typeAProp, item.otherAProp, item.sameProp) // valid
  }

  // item.typeAProp // invalid
  // item.sameProp // valid
  ...
}

function isMyTypeA(item: MyTypeA | MyTypeB): item is MyTypeA {
  // Return a condition, that guarantees your type is what you are after
  return (item as MyTypeA).typeAProp !== undefined;
}

πŸ’” Partial

Partial will set all properties (except nested properties, we'll touch on this in a bit) as optional.

type MyType = {
  RequiredProp1: string;
  RequiredProp2: number;
  OptionalProp1?: boolean;
}

type PartialMyType = Partial<MyType>;

// This will produce an error, as RequiredProp1 is missing
const item: MyType = {
  RequiredProp2: 42
}


// This is valid, as partial marked everything as optional
const partialItem: PartialMyType = {
  RequiredProp2: 42
}

As you can see from the example above, Partial saves us from having to define a nearly duplicate type and marking everything as optional ourselves.

Note there is a built in utility method that does the opposite of Partial called Required. Its usage is identical to the above.

Bonus To apply partial to your nested types recursively, use the following snippet

type RecursivePartial<T> = { [P in keyof T]?: RecursivePartial<T[P]> };

// Usage
type EverythingPartial = RecursivePartial<MyType>;

⛏ Pick & Omit

Pick & Omit are useful when it comes to creating a sub type from an external (or internal) interface or types.

// external.ts
interface ExternalInterface {
  name: string;
  address: string;
  age: number;
}

// mycode.ts

// Pick
type MyPickType = Pick<ExternalInterface, 'name' | 'age'>;


// Omit
type MyOmitType = Omit<ExternalInterface, 'address'>;

Both MyPickType and MyOmitType end up consisting of the same properties. As the term Pick suggests, you pick which properties you want on your new type. With Omit you exclude the properties you list.

Footnote

If I left out a hidden gem or if you need clarification on anything, let me know!

Proof read & edited by my beautiful wife ❀!

Remember to #KeepHavingFun

Top comments (0)

🌚 Life is too short to browse without dark mode