带有泛型的 Typescript 箭头函数的语法错误
Posted
技术标签:
【中文标题】带有泛型的 Typescript 箭头函数的语法错误【英文标题】:Syntax error for Typescript arrow functions with generics 【发布时间】: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'
);
在这个例子中,我把<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):
https://github.com/teppeis/typescript-spec-md/blob/master/en/ch04.md https://basarat.gitbooks.io/typescript/content/docs/types/generics.html https://github.com/Microsoft/TypeScript/issues/3323 Specify return type in TypeScript arrow function但我仍然想知道这个问题的确切原因。
【问题讨论】:
【参考方案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 方开源吗?最好至少在那里打开一个问题,以便作者可以修复它。以上是关于带有泛型的 Typescript 箭头函数的语法错误的主要内容,如果未能解决你的问题,请参考以下文章
带有泛型的 Typescript JSX - 参数隐式具有“任何”类型
如何在带有 React 的 Typescript/JSX 中使用带有箭头函数的泛型?