0

Consider the following code:

const subscriptions: { [key: string]: [() => void]  } = {}

interface ISubscription {
  eventName: string
  callback: () => void
}

export function unsubscribe({ eventName, callback }: ISubscription) {
  if (!subscriptions[eventName]) { return }
  const index = subscriptions[eventName].findIndex((l) => l === callback)
  if (index < 0) { return }
  subscriptions[eventName].splice(index, 1)
}

export function subscribe({ eventName, callback }: ISubscription) {
  if (!subscriptions[eventName]) {
    subscriptions[eventName] = []
  }
  subscriptions[eventName].push(callback)
  return () => unsubscribe({ eventName, callback })
}

The issue typescript reports is:

TS2741: Property '0' is missing in type '[]' but required in type '[() => void]'.

Which comes from trying to assign an empty array to the subscriptions:

if (!subscriptions[eventName]) {
  subscriptions[eventName] = []
}

This can be fixed by defining the interface to accept an empty array but then there's an issue when assigning a callback:

const subscriptions: { [key: string]: [() => void] | [] } = {}

TS2345: Argument of type '() => void' is not assignable to parameter of type 'never'.

A workaround would be:

const subscriptions: { [key: string]: [() => void] } = {}

if (!subscriptions[eventName]) {
  subscriptions[eventName] = [callback]
}
else {
  subscriptions[eventName].push(callback)
}

What is the correct way to be able to set it to an empty array and then assign the callback? I tried using the question mark too but I can't seem to get it right. Thank you for your help.

1
  • Does this help? Commented Aug 12, 2020 at 14:09

3 Answers 3

2

This is a common misconception. [() => void] is a tuple of one element, and that element needs to satisfy the type () => void. If you want an array, let it be empty or not, with one or multiple elements, the correct type is (() => void)[].

The following expressions are accepted by (() => void)[]:

  1. []
  2. [() => { doSomething(); }]
  3. [() => { doSomething(); }, () => { doSomethingElse(); }]

While only the expression number 2 is accepted by [() => void].

If you want either an empty array or a tuple of one element, you can just do [] | [() => void]

Sign up to request clarification or add additional context in comments.

Comments

1

You can simply define it as an "array of type":

type Subscriptions = {
  [key: string]: Array<()=>void>
}

then:

const subscriptions: Subscriptions = {}
if (!subscriptions[eventName]) {
  subscriptions[eventName] = []
}

Comments

1

Pretty straight-forward solution

type TCallBackArray = (() => void)[];
interface ISubscriptions { 
    [key: string]: TCallBackArray
};

const subscriptions: ISubscriptions = {}

Where TCallBackArray is defined as an array of functions, and ISubscriptions is defined as an indexer.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.