【问题标题】:Syntax error for Typescript arrow functions with generics带有泛型的 Typescript 箭头函数的语法错误
【发布时间】:2018-01-31 06:14:22
【问题描述】:

首先这里有一个类似的问题:what-is-the-syntax-for-typescript-arrow-functions-with-generics

但是,我想知道语法错误的罪魁祸首。

我正在使用外部库,这是定义文件 (index.d.ts) 的样子:


外部库的 index.d.ts

declare namespace Student {
    export interface Lecture {
        lectureName: string;
    }

    export interface Student {
        new (): Student;

        on1(eventName: string, callback: (<T>(lecture: T, oldLecture: T) => void) |
                                        ((name: string, ...args: any[]) => void)): void;

        on2(eventName: string, callback: (<T>(lecture: T, oldLecture: T) => void)): void;
    }
}

declare var Student: Student.Student;

declare module "student" {
    export = Student;
}

注意Student.Student中有两个函数:on1和on2——函数on1有多一点代码。

这是我的代码示例。


案例一

import * as Student from 'student';
import { Lecture } from 'student';

export class MyStudent { 
    student: Student.Student;

    constructor() {
        this.student = new Student();

        this.student.on1('test', (lecture: Lecture, oldLecture: Lecture) => {
            // Argument of type error
        });

        this.student.on2('test', (lecture: Lecture, oldLecture: Lecture) => {
            // Argument of type error
        });
    }
}

函数 on1 给出以下错误:

'(lecture: Lecture, oldLecture: Lecture) => void' 类型的参数不能分配给 '((lecture: T, oldLecture: T) => void) 类型的参数 | ((name: string, ...args: any[]) => void)'。 类型 '(lecture: Lecture, oldLecture: Lecture) => void' 不可分配给类型 '(name: string, ...args: any[]) => void'。 参数 'lecture' 和 'name' 的类型不兼容。 类型“字符串”不可分配给类型“讲座”。

函数 on2 给出以下错误:

“(lecture: Lecture, oldLecture: Lecture) => void”类型的参数不能分配给“(lecture: T, oldLecture: T) => void”类型的参数。 参数 'lecture' 和 'lecture' 的类型不兼容。 类型“T”不能分配给类型“Lecture”。

我认为这个示例是实现代码的正确方法 - 但为什么会报错?


案例 2

import * as Student from 'student';
import { Lecture } from 'student';

export class MyStudent { 
    student: Student.Student;

    constructor() {
        this.student = new Student();

        this.student.on1('test', <Lecture>(lecture: Lecture, oldLecture: Lecture) => {
            lecture.lectureName; 
            // Error: Property 'lectureName' does not exist on type 'Lecture'
        });

        this.student.on2('test', <Lecture>(lecture: Lecture, oldLecture: Lecture) => {
            lecture.lectureName;
            // Error: Property 'lectureName' does not exist on type 'Lecture'
        });
    }
}

在这个例子中,我把&lt;Lecture&gt;放在了箭头函数的前面——所以实现中没有错误,但现在我根本不能使用lecture.lectureName。为什么?


案例 3

import * as Student from 'student';
import { Lecture } from 'student';

export class MyStudent { 
    student: Student.Student;

    constructor() {
        this.student = new Student();

        this.student.on1('test', <T extends Lecture>(lecture: T, oldLecture: T) => {
            lecture.lectureName; // Yay! No problem!
        });

        this.student.on2('test', <T extends Lecture>(lecture: T, oldLecture: T) => {
            // Argument of type error
        });
    }
}

所以这个例子有正确答案 - 但是,函数 on2 仍然给出类型错误的参数,就像案例 1 的例子一样。既然函数on1没问题,应该没问题吧?


案例4

import * as Student from 'student';
import { Lecture } from 'student';

export class MyStudent { 
    student: Student.Student;

    constructor() {
        this.student = new Student();

        this.student.on1('test', () => () => (lecture: Lecture, oldLecture: Lecture) => {
            lecture.lectureName; // Yay! No error!
        });

        this.student.on2('test', () => () => (lecture: Lecture, oldLecture: Lecture) => {
            lecture.lectureName; // Yay! No error!
        });
    }
}

我偶然发现了这个解决方案 - 两个功能都运行良好。但我不知道为什么会这样。


我花了一些时间试图通过查看这些参考来找出确切的原因(因为我喜欢 TypeScript):

但我仍然想知道这个问题的确切原因。

【问题讨论】:

    标签: typescript generics typescript-typings arrow-functions


    【解决方案1】:

    我知道这不能直接回答你的问题,但你可以通过使用类方法来避免这个问题,而不是嵌套匿名回调(感觉很 2015)

    type Handler = <T>(t: T) => void;
    
    class Student {
      on1(s:string, callback:Handler) : void {
        callback<string>("hi")
      }
    }
    
    class MyStudent { 
        student: Student
    
        constructor() {
            this.student = new Student()
            this.student.on1('test', this.log)
        }
    
        log<T>(t:T) : void { 
             console.log("hi " + t)
        }
    }
    

    【讨论】:

      【解决方案2】:

      您对如何声明泛型函数以及如何调用泛型函数感到有些困惑。

      你可以用这个总结你的问题:

      // Define the Identity function type
      // The result type = input type
      type TIdentityFunc = <T>(input: T) => T;
      
      // Implement the TIdentity function
      // We followed the rule.
      const identityImpl: TIdentityFunc = <T>(input: T) => input;
      
      // Now we call this implementation
      const num = identity(5);   // num is always number
      const str = identity('hi') // str is always a string
      

      在您的示例中,您实现了请求的回调,这意味着当有人调用此回调时,她将知道参数类型。

      记住,你不是在调用回调,你只是在实现它!

      所以你的代码应该是这样的:

      import * as Student from 'student';
      import { Lecture } from 'student';
      
      export class MyStudent {
        student: Student.Student;
      
        constructor() {
          this.student = new Student();
      
          this.student.on1('test', <T>(l1: T | string, l2: T, ...args) => {
            // This is a bit complicated overloading, 
            // But it follows the rules of the declaration
          });
      
          this.student.on2('test', <T>(lecture: T, oldLecture: T) => {
            // Your only assumption is that lecture, and oldLecture are the same type
          });
        }
      }
      

      【讨论】:

      • 非常感谢您的评论和示例代码!我有一个问题 - 在你的例子中,我怎样才能写出没有错误的代码?例如,如果我写了这样的代码:lecture.lectureName,它表示该属性不存在,就像案例 2。
      • 根据您的声明,您应该使用两个具有相同类型的参数来实现回调,仅此而已。什么都没有说 Lecture 对象...这就是 TypeScript 告诉您您未实现请求的声明的原因。顺便说一句:在案例 2 中,您只是将通用命名为“Lecture”而不是“T”。请记住,您不是此函数的调用者,您只需要实现此通用函数。调用者将使用两个具有相同类型的参数调用您的回调。
      【解决方案3】:

      问题是打字在错误的地方声明了泛型:

      declare interface Lecture {
        lectureName: string;
      }
      
      declare interface Student {
        new (): Student;
      
        on1<T>(eventName: string, callback: ((lecture: T, oldLecture: T) => void) |
          ((name: string, ...args: any[]) => void)): void;
        on2<T>(eventName: string, callback: (lecture: T, oldLecture: T) => void): void;
      }
      
      let s: Student;
      
      s.on1('x', (a: Lecture, b: Lecture) => {
      
      })
      s.on2('y', (a: string, b: string) => {
      
      })
      

      【讨论】:

      • 感谢您的评论 - 但不幸的是,我不能(也不想)更改声明文件,因为它是第 3 方库。另外,代码declare interface 将覆盖现有定义
      • 其他人会遇到与您相同的问题。这是第 3 方开源吗?最好至少在那里打开一个问题,以便作者可以修复它。
      猜你喜欢
      • 2015-11-25
      • 2021-12-25
      • 2019-06-08
      • 2017-04-27
      • 2019-12-17
      • 1970-01-01
      • 2017-10-14
      • 1970-01-01
      相关资源
      最近更新 更多