简体   繁体   English

类型中缺少属性,但类型中需要属性

[英]Property is missing in type but required in type

I'm trying to implement simple event dispatcher on TypeScript.我正在尝试在 TypeScript 上实现简单的事件调度程序。

My custom types are declared like this:我的自定义类型声明如下:

events.d.ts : events.d.ts

type AppEvent = { [key: string]: any }

type AppEventListener<T> = <T extends AppEvent>(event: T) => void;

When I pass any object in this function当我在这个 function 中通过任何 object

   public addListener<T extends AppEvent>(event: T, listener: AppEventListener<T>)

I get the following error:我收到以下错误:

Argument of type '(event: TestEvent) => void' is not assignable to parameter of type 'AppEventListener<typeof TestEvent>'.
   Types of parameters 'event' and 'event' are incompatible.
     Type 'T' is not assignable to type 'TestEvent'.
       Property 'name' is missing in type 'AppEvent' but required in type 'TestEvent'.

So, this type AppEvent cannot have any additional properties.所以,这种类型的AppEvent不能有任何额外的属性。 How to make it work?如何让它发挥作用? Any help appreciated.任何帮助表示赞赏。

Full code example:完整代码示例:

class EventDispatcher {
  private listeners: Map<AppEvent, Array<AppEventListener<AppEvent>>>

  constructor() {
    this.listeners = new Map();
  }

  public dispatch<T extends AppEvent>(event: T): void {
    const listeners = this.listeners.get(event.constructor);
    if (listeners) {
      listeners.forEach((callback) => {
        callback(event);
      });
    }
  }

  public addListener<T extends AppEvent>(event: T, listener: AppEventListener<T>): void {
    let listeners = this.listeners.get(event);
    if (!listeners) {
      listeners = [listener];
      this.listeners.set(event, listeners);
    } else {
      listeners.push(listener);
    }
  }
}

class TestEvent {
  public name: string = ''
}

const dispatcher = new EventDispatcher();
const listener = (event: TestEvent) => {
  console.dir(event);
};
dispatcher.addListener(TestEvent, listener);

const event = new TestEvent('name');
dispatcher.dispatch(event);

It looks like you should make AppEventLister a generic type referring to a non-generic function, like this:看起来你应该让AppEventLister成为泛型类型,引用非泛型 function,如下所示:

type AppEventListener<T> = (event: T) => void;

You're saying "an AppEventListener<T> will take an event of type T ".您是说“ AppEventListener<T>将接受T类型的事件”。 That's what you want, right?这就是你想要的,对吧?


Your original definition was a generic type referring to a generic function, with two different generic parameters of the same name, equivalent to您的原始定义是泛型类型,指的是泛型 function,具有两个不同的同名泛型参数,相当于

type AppEventListenerBad<T> = <U>(event: U) => void;

(the rename doesn't change the type; it just makes makes more clear what was happening with type parameter name shadowing) meaning "an AppEventListener<T> will ignore T entirely and take an event of any type U that the caller wants to specify", which is unlikely what you mean. (重命名不会更改类型;它只是让类型参数名称阴影发生的情况更清楚)意味着“ AppEventListener<T>将完全忽略T并接受调用者想要指定的任何类型U的事件",这不太可能是你的意思。 And that's where your error is coming from: (event: TestEvent) => void is not assignable to <U>(event: U) => void .这就是您的错误的来源: (event: TestEvent) => void不可分配给<U>(event: U) => void


Once you do this, a lot of other issues are solved, but they might bring up other problems.一旦你这样做了,很多其他的问题就解决了,但它们可能会带来其他的问题。 A major one is that the standard library typings for Map represent a very basic mapping from keys of type K to values of type V .一个主要的问题是Map的标准库类型表示从K类型的键到V类型的值的非常基本的映射。 It does not have a way to say that keys of certain subtypes of K map to values of programmatically determined subtypes of V .它没有办法说K map 的某些子类型的键与以编程方式确定的V子类型的值。 In your case you could make a new interface for the way you are using Map :在您的情况下,您可以为使用Map的方式创建一个新界面:

interface AppEventListenerMap {
  get<T>(x: T): Array<AppEventListener<T>> | undefined;
  set<T>(x: T, val: Array<AppEventListener<T>>): void;
}

And then in EventDispatcher you make the listeners property a value of type AppEventListenerMap :然后在EventDispatcher中将listeners属性AppEventListenerMap类型的值:

class EventDispatcher {
  private listeners: AppEventListenerMap;

  constructor() {
    this.listeners = new Map();
  }

That will mostly make your code example work, except for the fact that you seem to be using event as the key sometimes, and using event.constructor as the key other times.这将主要使您的代码示例工作,除了您似乎有时使用event作为键,而在其他时候使用event.constructor作为键这一事实。 That inconsistency seems wrong, and you probably need to decide exactly the type relationship to get it working properly.这种不一致似乎是错误的,您可能需要准确地确定类型关系才能使其正常工作。

Anyway, hopefully this helps;无论如何,希望这会有所帮助; good luck!祝你好运!

Playground link to code Playground 代码链接

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 类型“{}”中缺少属性“submitAction”,但在类型中是必需的 - Property 'submitAction' is missing in type '{}' but required in type 类型中缺少 TypeScript 属性“导航”,但类型“道具”React Native 中需要 - TypeScript Property 'navigation' is missing in type but required in type 'Props' React Native 类型“ any []”中缺少属性“ 0”,但是类型“ [{{id:string; gp:布尔值; }] - Property '0' is missing in type 'any[]' but required in type '[{ id: string; gp: boolean; }] “ReactElement”类型中缺少属性“...”<any, any> ' 但在类型 '{...}' 中是必需的</any,> - Property '...' is missing in type 'ReactElement<any, any>' but required in type '{...}' 类型“{}”中缺少属性“profileStore”,但类型“只读”中需要<AppProps> &#39;.ts(2741) - Property 'profileStore' is missing in type '{}' but required in type 'Readonly<AppProps>'.ts(2741) Typescript 错误:类型中缺少属性“children”,但类型“CommonProps”中是必需的 - Typescript error: Property 'children' is missing in type but required in type 'CommonProps' '{ rating: any; 类型中缺少属性 'onClick' }' 但在“RatingType”类型中是必需的 - Property 'onClick' is missing in type '{ rating: any; }' but required in type 'RatingType' “文件[]”类型中缺少属性“项目”,但在“文件列表”类型中是必需的 - Property 'item' is missing in type 'File[]' but required in type 'FileList' 类型'import(“cesium”).BoundingSphere'中缺少属性'intersect',但在类型中是必需的 - Property 'intersect' is missing in type 'import(“cesium”).BoundingSphere' but required in type 属性“儿童”的类型缺失 - Property 'children' is missing in type
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM