使用泛型

时间:2017-08-22 20:29:55

标签: typescript generics typescript-typings arrow-functions

首先,这里有一个类似的问题: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;
}

请注意,有两个功能: on1 on2 Student.Student - 函数 on1 多一点代码。

所以这是我的代码示例。

<案例1
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 给出以下错误:

  

类型的论点'(讲座:讲座,old讲义:讲座)=&gt; void'不能赋予类型'的参数((讲座:T,oldLecture:T)=&gt; void)| ((name:string,... args:any [])=&gt; void)'。     类型'(讲座:讲座,oldLecture:Lecture)=&gt; void'不能赋值给''(name:string,... args:any [])=&gt;无效”。       参数“讲座”和“名称”的类型不兼容。         类型'string'不能指定为'Lecture'类型。

函数 on2 给出以下错误:

  

类型的论点'(讲座:讲座,old讲义:讲座)=&gt; void'不能分配给类型'的参数(讲座:T,oldLecture:T)=&gt;无效”。     参数'讲座'和'讲座'的类型是不相容的。       类型'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'
        });
    }
}

在这个例子中,我将<Lecture>放在箭头函数前面 - 所以实现中没有错误,但现在我根本不能使用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):

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

3 个答案:

答案 0 :(得分: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)
    }
}

答案 1 :(得分:1)

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

您可以用以下方式总结您的问题:

// 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
    });
  }
}

答案 2 :(得分:1)

问题是打字是在错误的地方声明通用:

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) => {

})