Typescript deep dive topics

💻Programming Language
javascript
typescript

void vs. never

// !-------------------
// ! `void` and `never`
// !-------------------

type NeverOne = boolean & never;
type NeverTwo = boolean | never;

// `never` if the function never ends
const boolOrThrow = (): never => {
  if (Math.random() > 0.5) {
    throw new Error("hello");
  }
  throw new Error("bye");
}

type sth = number | never;

// `number` if it returns
const numberFunction = (): number => {
  if (Math.random() > 0.5) {
    throw new Error("hello");
  } else {
    return 1;
  }
}

// `void` if it doesn't explicitly return. `void` prevents a function to set a `return` statement.
const voidFunction = (): void => {
  if (Math.random() > 0.5) {
    throw new Error("hello");
  }
}

Optional prop values

Attention: Optional prop values implicitly become an union with undefined.

// !---------------------
// ! optional prop values
// !---------------------

type EntityOptional = {
  id?: number, // (property) id?: number | undefined
}

// Optional properties may be undefined
const entityOptional: EntityOptional = { id: undefined };

Indexed Access Type problematic with optional values

Attention: Indexed access type inherit optional state from property

type EntityOptional = {
  id?: number, // implicit type: id?: number | undefined
}

type EntityCopy = {
  id: EntityOptional['id'], // implicit type taken: number | undefined (not just number!)
}

const entity: EntityCopy = { id: undefined }; // Attention! `id` in `EntityCopy` may still be 'undefined'
const entity2: EntityCopy = {}; // error: You need an 'id' now. It's not optional.However, it may still be undefined.

Attention: Optional values remain optional with indexed access type - fixed with Omit and intersection!

// models.ts
type Person = {
  id?: number,
  name: string,
  hobbies: string[]
}

//
type PersonWithNonOptionalId = {
  id: Person['id'],	// Attention! This `id` is still optional!
  name: string,
  hobbies: string[]
}

// Right way! Person with non-optional number id
type PersonSyncedWithBackend = Omit<Person, 'id'> & { id: number };

See also this TS playground with a similar example.

Discuss on TwitterImprove this article: Edit on GitHub

Discussion


Explain Programming

André Kovac builds products, creates software, teaches coding, communicates science and speaks at events.