web-dev-qa-db-fra.com

L'argument de type '(e: CustomEvent) => void' n'est pas assignable au paramètre de type 'EventListenerOrEventListenerObject'

J'ai cette configuration d'événement personnalisé, et cela fonctionne avec TypeScript 2.5.3, mais lorsque j'ai mis à jour à 2.6.1, une erreur s'est produite.

window.addEventListener('OnRewards', (e: CustomEvent) => {
    // my code here
})

[ts] L'argument de type '(e: CustomEvent) => void' n'est pas assignable au paramètre de type 'EventListenerOrEventListenerObject'.

Le type '(e: CustomEvent) => void' n'est pas assignable au type 'EventListenerObject'.

La propriété 'handleEvent' est manquante dans le type '(e: CustomEvent) => void'.

Je ne sais pas exactement quoi faire ici pour résoudre ce problème.

5
Get Off My Lawn

Cela est dû au comportement de l'indicateur --strictFunctionTypes compiler ajouté dans TypeScript v2.6. Une fonction de type (e: CustomEvent) => void n'est plus considérée comme une instance valide de EventListener, qui prend un paramètre Event, pas une CustomEvent

Donc, un moyen de résoudre ce problème consiste à désactiver --strictFunctionTypes.


Une autre méthode consiste à transmettre une fonction qui prend une Event et se réduit ensuite à CustomEvent via un garde type:

function isCustomEvent(event: Event): event is CustomEvent {
  return 'detail' in event;
}

window.addEventListener('OnRewards', (e: Event) => {
  if (!isCustomEvent(e))
    throw new Error('not a custom event');
  // e is now narrowed to CustomEvent ...
  // my code here 
})

Une troisième méthode consiste à utiliser l’autre surcharge de addEventListener():

addEventListener<K extends keyof WindowEventMap>(type: K, listener: (this: Window, ev: WindowEventMap[K]) => any, useCapture?: boolean): void;

Si le paramètre type est le nom d'un type d'événement connu (K extends keyof WindowEventMap) tel que "onclick", la fonction listener s'attend à ce que son paramètre soit de ce type d'événement restreint (WindowEventMap[K]). Le problème est que "OnRewards" n'est pas un type d'événement connu ... sauf si vous utilisez déclaration fusionnant avec make le connu:

// merge into WindowEventMap
interface WindowEventMap {
    OnRewards: CustomEvent
}

Ou, si vous êtes dans un module (quelque chose contenant export dans celui-ci), utilisez augmentation globale :

// merge into WindowEventMap
declare global {
  interface WindowEventMap {
    OnRewards: CustomEvent
  }
}

Ensuite, utilisez votre code comme avant:

// no error!
window.addEventListener('OnRewards', (e: CustomEvent) => {
    // my code here
})

Donc, ce sont vos options. Lequel vous voulez choisir est à vous. J'espère que cela pourra aider; bonne chance!

9
jcalz

En vous basant sur l'excellente réponse de jcalz , vous pouvez également utiliser une assertion de type pour être un peu plus propre:

window.addEventListener('OnRewards', (e: Event) => {
    const detail = (<CustomEvent>e).detail;
    ...
});
3
JacobEvelyn

Vous avez aussi cette option:

window.addEventListener('OnRewards', (e: CustomEvent) => {
    // your code here
} as (e: Event) => void)
0
Frederik Krautwald

J'ai créé une fonction générique basée sur la réponse de @ jcalz 

/**
 * Checks whether an object can be safely cast to its child type
 * @param parent the object to be 'narrowly' cast down to its child type
 * @param checkForProps props which aught to be present on the child type
 */
export function isSubTypeWithProps<P, C extends P>(parent: P, ...checkForProps: (keyof C)[]): parent is C {
  return checkForProps.every(prop => prop in parent);
}

/**
 * Usage example
 */
const el = document.getElementById('test');
el.addEventListener('click', (e: Event) => {
  if (isSubTypeWithProps<Event, MouseEvent>(e, 'which')) {
    if (e.which === 1) { // primary mouse button only ('which' prop is only available on MouseEvent)
      console.log('clicked');
    }
  }
});
0
Stephen Paul